| Index: src/profiler/sampling-heap-profiler.cc
|
| diff --git a/src/profiler/sampling-heap-profiler.cc b/src/profiler/sampling-heap-profiler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4fff10efa86d0940425b9d124408011371a6578f
|
| --- /dev/null
|
| +++ b/src/profiler/sampling-heap-profiler.cc
|
| @@ -0,0 +1,188 @@
|
| +// Copyright 2015 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "src/api.h"
|
| +#include "src/frames-inl.h"
|
| +#include "src/heap/heap.h"
|
| +#include "src/isolate.h"
|
| +#include "src/profiler/sampling-heap-profiler.h"
|
| +#include "src/profiler/strings-storage.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +SamplingHeapProfiler::SamplingHeapProfiler(Heap* heap, StringsStorage* names,
|
| + uint64_t rate, int stack_depth)
|
| + : InlineAllocationObserver(GetNextSampleInterval(
|
| + heap->isolate()->random_number_generator(), rate)),
|
| + isolate_(heap->isolate()),
|
| + heap_(heap),
|
| + random_(isolate_->random_number_generator()),
|
| + names_(names),
|
| + samples_(),
|
| + rate_(rate),
|
| + stack_depth_(stack_depth) {
|
| + heap->new_space()->AddInlineAllocationObserver(this);
|
| +}
|
| +
|
| +
|
| +SamplingHeapProfiler::~SamplingHeapProfiler() {
|
| + heap_->new_space()->RemoveInlineAllocationObserver(this);
|
| +
|
| + // Clear samples and drop all the weak references we are keeping.
|
| + std::set<SampledAllocation*>::iterator it;
|
| + for (it = samples_.begin(); it != samples_.end(); ++it) {
|
| + delete *it;
|
| + }
|
| + std::set<SampledAllocation*> empty{};
|
| + samples_.swap(empty);
|
| +}
|
| +
|
| +void SamplingHeapProfiler::Step(int bytes_allocated, Address soon_object,
|
| + size_t size) {
|
| + DCHECK(heap_->gc_state() == Heap::NOT_IN_GC);
|
| + DCHECK(soon_object);
|
| + SampleObject(soon_object, size);
|
| +}
|
| +
|
| +
|
| +void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) {
|
| + DisallowHeapAllocation no_allocation;
|
| +
|
| + HandleScope scope(isolate_);
|
| + HeapObject* heap_object = HeapObject::FromAddress(soon_object);
|
| + Handle<Object> obj(heap_object, isolate_);
|
| +
|
| + // Mark the new block as FreeSpace to make sure the heap is iterable while we
|
| + // are taking the sample.
|
| + heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size));
|
| +
|
| + Local<v8::Value> loc = v8::Utils::ToLocal(obj);
|
| +
|
| + SampledAllocation* sample =
|
| + new SampledAllocation(this, isolate_, loc, size, stack_depth_);
|
| + samples_.insert(sample);
|
| +}
|
| +
|
| +
|
| +// We sample with a Poisson process, with constant average sampling interval.
|
| +// This follows the exponential probability distribution with parameter
|
| +// λ = 1/rate where rate is the average number of bytes between samples.
|
| +//
|
| +// Let u be a uniformly distributed random number between 0 and 1, then
|
| +// next_sample = (- ln u) / λ
|
| +intptr_t SamplingHeapProfiler::GetNextSampleInterval(
|
| + base::RandomNumberGenerator* random, uint64_t rate) {
|
| + double u = random->NextDouble();
|
| + double next = (-std::log(u)) * rate;
|
| + return std::max(static_cast<intptr_t>(kPointerSize),
|
| + static_cast<intptr_t>(next));
|
| +}
|
| +
|
| +
|
| +void SamplingHeapProfiler::GetHeapSample(OutputStream* stream) {
|
| + std::map<int, Script*> scripts;
|
| + {
|
| + Script::Iterator iterator(isolate_);
|
| + Script* script;
|
| + while ((script = iterator.Next())) {
|
| + scripts[script->id()] = script;
|
| + }
|
| + }
|
| +
|
| + OutputStreamWriter writer(stream);
|
| + writer.AddString("[\n");
|
| + std::set<SampledAllocation*>::iterator it;
|
| + for (it = samples_.begin(); it != samples_.end(); ++it) {
|
| + auto sample = *it;
|
| + if (it != samples_.begin()) {
|
| + writer.AddString(",");
|
| + }
|
| + writer.AddString(" {\"size\": ");
|
| + writer.AddNumber(static_cast<unsigned>(sample->get_size()));
|
| + writer.AddString(", \"stack\": [\n");
|
| + List<FunctionInfo*>& stack = sample->get_stack();
|
| + for (int i = 0; i < stack.length(); ++i) {
|
| + FunctionInfo* info = stack[i];
|
| + int line = -1;
|
| + int column = -1;
|
| + if (info->script_id != -1) {
|
| + Handle<Script> script(scripts[info->script_id]);
|
| + line = Script::GetLineNumber(script, info->start_position);
|
| + column = Script::GetColumnNumber(script, info->start_position);
|
| + }
|
| +
|
| + writer.AddString("\t{\"name\": \"");
|
| + writer.AddString(info->name);
|
| + writer.AddString("\"");
|
| + if (line != -1) {
|
| + DCHECK(column != -1);
|
| + writer.AddString(", \"line\": ");
|
| + writer.AddNumber(line);
|
| + writer.AddString(", \"column\": ");
|
| + writer.AddNumber(column);
|
| + }
|
| + writer.AddString(", \"scriptName\": \"");
|
| + writer.AddString(info->script_name);
|
| + if (i < (stack.length() - 1)) {
|
| + writer.AddString("\"},\n");
|
| + } else {
|
| + writer.AddString("\"}\n");
|
| + }
|
| + }
|
| + writer.AddString(" ]}\n");
|
| + }
|
| + writer.AddString("]\n");
|
| + writer.Finalize();
|
| +}
|
| +
|
| +void SamplingHeapProfiler::SampledAllocation::OnWeakCallback(
|
| + const WeakCallbackInfo<SampledAllocation>& data) {
|
| + SampledAllocation* sample = data.GetParameter();
|
| + sample->shp_->samples_.erase(sample);
|
| + delete sample;
|
| +}
|
| +
|
| +
|
| +SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared,
|
| + StringsStorage* names)
|
| + : name(names->GetFunctionName(shared->DebugName())),
|
| + script_name(""),
|
| + script_id(-1),
|
| + start_position(-1) {
|
| + if (shared->script()->IsScript()) {
|
| + Script* script = Script::cast(shared->script());
|
| + script_id = script->id();
|
| + start_position = shared->start_position();
|
| + if (script->name()->IsName()) {
|
| + Name* name = Name::cast(script->name());
|
| + script_name = names->GetName(name);
|
| + }
|
| + }
|
| +}
|
| +
|
| +SamplingHeapProfiler::SampledAllocation::SampledAllocation(
|
| + SamplingHeapProfiler* shp, Isolate* isolate, Local<Value> local,
|
| + size_t size, int max_frames)
|
| + : shp_(shp),
|
| + global_(reinterpret_cast<v8::Isolate*>(isolate), local),
|
| + size_(size) {
|
| + global_.SetWeak(this, OnWeakCallback, WeakCallbackType::kParameter);
|
| +
|
| +
|
| + StackTraceFrameIterator it(isolate);
|
| + int frames_captured = 0;
|
| + while (!it.done() && frames_captured < max_frames) {
|
| + JavaScriptFrame* frame = it.frame();
|
| + SharedFunctionInfo* shared = frame->function()->shared();
|
| + stack_.Add(new FunctionInfo(shared, shp->names()));
|
| +
|
| + frames_captured++;
|
| + it.Advance();
|
| + }
|
| +}
|
| +
|
| +
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|