5 #include "src/profiler/sampling-heap-profiler.h" 10 #include "src/api-inl.h" 11 #include "src/base/ieee754.h" 12 #include "src/base/template-utils.h" 13 #include "src/base/utils/random-number-generator.h" 14 #include "src/frames-inl.h" 15 #include "src/heap/heap.h" 16 #include "src/isolate.h" 17 #include "src/profiler/strings-storage.h" 28 intptr_t SamplingAllocationObserver::GetNextSampleInterval(uint64_t rate) {
29 if (FLAG_sampling_heap_profiler_suppress_randomness) {
30 return static_cast<intptr_t
>(rate);
32 double u = random_->NextDouble();
33 double next = (-base::ieee754::log(u)) * rate;
34 return next < kPointerSize
36 : (next > INT_MAX ? INT_MAX :
static_cast<intptr_t
>(next));
47 size_t size,
unsigned int count)
const {
48 double scale = 1.0 / (1.0 - std::exp(-static_cast<double>(size) / rate_));
50 return {size,
static_cast<unsigned int>(count * scale + 0.5)};
53 SamplingHeapProfiler::SamplingHeapProfiler(
54 Heap* heap, StringsStorage* names, uint64_t rate,
int stack_depth,
55 v8::HeapProfiler::SamplingFlags flags)
56 : isolate_(heap->isolate()),
58 new_space_observer_(new SamplingAllocationObserver(
59 heap_, static_cast<intptr_t>(rate), rate, this,
60 heap->isolate()->random_number_generator())),
61 other_spaces_observer_(new SamplingAllocationObserver(
62 heap_, static_cast<intptr_t>(rate), rate, this,
63 heap->isolate()->random_number_generator())),
65 profile_root_(nullptr,
"(root)",
v8::UnboundScript::kNoScriptId, 0,
67 stack_depth_(stack_depth),
71 heap_->AddAllocationObserversToAllSpaces(other_spaces_observer_.get(),
72 new_space_observer_.get());
75 SamplingHeapProfiler::~SamplingHeapProfiler() {
76 heap_->RemoveAllocationObserversFromAllSpaces(other_spaces_observer_.get(),
77 new_space_observer_.get());
80 void SamplingHeapProfiler::SampleObject(Address soon_object,
size_t size) {
81 DisallowHeapAllocation no_allocation;
83 HandleScope scope(isolate_);
84 HeapObject* heap_object = HeapObject::FromAddress(soon_object);
85 Handle<Object> obj(heap_object, isolate_);
89 heap_->CreateFillerObjectAt(soon_object, static_cast<int>(size),
90 ClearRecordedSlots::kNo);
92 Local<v8::Value> loc = v8::Utils::ToLocal(obj);
94 AllocationNode* node = AddStack();
95 node->allocations_[size]++;
97 base::make_unique<Sample>(size, node, loc,
this, next_sample_id());
98 sample->global.SetWeak(sample.get(), OnWeakCallback,
99 WeakCallbackType::kParameter);
101 #pragma clang diagnostic push 102 #pragma clang diagnostic ignored "-Wdeprecated" 106 sample->global.MarkIndependent();
108 #pragma clang diagnostic pop 110 samples_.emplace(sample.get(), std::move(sample));
113 void SamplingHeapProfiler::OnWeakCallback(
114 const WeakCallbackInfo<Sample>& data) {
115 Sample* sample = data.GetParameter();
116 AllocationNode* node = sample->owner;
117 DCHECK_GT(node->allocations_[sample->size], 0);
118 node->allocations_[sample->size]--;
119 if (node->allocations_[sample->size] == 0) {
120 node->allocations_.erase(sample->size);
121 while (node->allocations_.empty() && node->children_.empty() &&
122 node->parent_ && !node->parent_->pinned_) {
123 AllocationNode* parent = node->parent_;
124 AllocationNode::FunctionId
id = AllocationNode::function_id(
125 node->script_id_, node->script_position_, node->name_);
126 parent->children_.erase(
id);
130 sample->profiler->samples_.erase(sample);
134 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::FindOrAddChildNode(
135 AllocationNode* parent,
const char* name,
int script_id,
136 int start_position) {
137 AllocationNode::FunctionId
id =
138 AllocationNode::function_id(script_id, start_position, name);
139 AllocationNode* child = parent->FindChildNode(
id);
141 DCHECK_EQ(strcmp(child->name_, name), 0);
144 auto new_child = base::make_unique<AllocationNode>(
145 parent, name, script_id, start_position, next_node_id());
146 return parent->AddChildNode(
id, std::move(new_child));
149 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::AddStack() {
150 AllocationNode* node = &profile_root_;
152 std::vector<SharedFunctionInfo*> stack;
153 JavaScriptFrameIterator it(isolate_);
154 int frames_captured = 0;
155 bool found_arguments_marker_frames =
false;
156 while (!it.done() && frames_captured < stack_depth_) {
157 JavaScriptFrame* frame = it.frame();
163 if (frame->unchecked_function()->IsJSFunction()) {
164 SharedFunctionInfo* shared = frame->function()->shared();
165 stack.push_back(shared);
168 found_arguments_marker_frames =
true;
173 if (frames_captured == 0) {
174 const char* name =
nullptr;
175 switch (isolate_->current_vm_state()) {
185 case BYTECODE_COMPILER:
186 name =
"(BYTECODE_COMPILER)";
201 return FindOrAddChildNode(node, name, v8::UnboundScript::kNoScriptId, 0);
206 for (
auto it = stack.rbegin(); it != stack.rend(); ++it) {
207 SharedFunctionInfo* shared = *it;
208 const char* name = this->names()->GetName(shared->DebugName());
209 int script_id = v8::UnboundScript::kNoScriptId;
210 if (shared->script()->IsScript()) {
211 Script* script = Script::cast(shared->script());
212 script_id = script->id();
214 node = FindOrAddChildNode(node, name, script_id, shared->StartPosition());
217 if (found_arguments_marker_frames) {
219 FindOrAddChildNode(node,
"(deopt)", v8::UnboundScript::kNoScriptId, 0);
226 AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node,
227 const std::map<
int, Handle<Script>>& scripts) {
230 node->pinned_ =
true;
231 Local<v8::String> script_name =
232 ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String(
""));
233 int line = v8::AllocationProfile::kNoLineNumberInfo;
234 int column = v8::AllocationProfile::kNoColumnNumberInfo;
235 std::vector<v8::AllocationProfile::Allocation> allocations;
236 allocations.reserve(node->allocations_.size());
237 if (node->script_id_ != v8::UnboundScript::kNoScriptId &&
238 scripts.find(node->script_id_) != scripts.end()) {
240 auto non_const_scripts =
241 const_cast<std::map<int, Handle<Script>
>&>(scripts);
242 Handle<Script> script = non_const_scripts[node->script_id_];
243 if (!script.is_null()) {
244 if (script->name()->IsName()) {
245 Name name = Name::cast(script->name());
246 script_name = ToApiHandle<v8::String>(
247 isolate_->factory()->InternalizeUtf8String(names_->GetName(name)));
249 line = 1 + Script::GetLineNumber(script, node->script_position_);
250 column = 1 + Script::GetColumnNumber(script, node->script_position_);
253 for (
auto alloc : node->allocations_) {
254 allocations.push_back(ScaleSample(alloc.first, alloc.second));
258 ToApiHandle<v8::String>(
259 isolate_->factory()->InternalizeUtf8String(node->name_)),
260 script_name, node->script_id_, node->script_position_, line, column,
261 node->id_, std::vector<v8::AllocationProfile::Node*>(), allocations});
267 for (
const auto& it : node->children_) {
269 TranslateAllocationNode(profile, it.second.get(), scripts));
271 node->pinned_ =
false;
276 if (flags_ & v8::HeapProfiler::kSamplingForceGC) {
277 isolate_->heap()->CollectAllGarbage(
278 Heap::kNoGCFlags, GarbageCollectionReason::kSamplingProfiler);
282 std::map<int, Handle<Script>> scripts;
284 Script::Iterator iterator(isolate_);
285 while (Script* script = iterator.Next()) {
286 scripts[script->id()] = handle(script, isolate_);
290 TranslateAllocationNode(profile, &profile_root_, scripts);
291 profile->samples_ = SamplingHeapProfiler::BuildSamples();
296 const std::vector<v8::AllocationProfile::Sample>
297 SamplingHeapProfiler::BuildSamples()
const {
298 std::vector<v8::AllocationProfile::Sample> samples;
299 samples.reserve(samples_.size());
300 for (
const auto& it : samples_) {
301 const Sample* sample = it.second.get();
303 sample->owner->id_, sample->
size, ScaleSample(sample->size, 1).
count,
std::vector< Node * > children