Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: src/profiler/sampling-heap-profiler.cc

Issue 1555553002: [profiler] Implement POC Sampling Heap Profiler (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: fix warning + minor cleanup in AllocateNode Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/profiler/sampling-heap-profiler.h"
6
7 #include <memory>
8 #include "src/api.h"
9 #include "src/frames-inl.h"
10 #include "src/heap/heap.h"
11 #include "src/isolate.h"
12 #include "src/profiler/strings-storage.h"
13
14 namespace v8 {
15 namespace internal {
16
17 SamplingHeapProfiler::SamplingHeapProfiler(Heap* heap, StringsStorage* names,
18 uint64_t rate, int stack_depth)
19 : InlineAllocationObserver(GetNextSampleInterval(
20 heap->isolate()->random_number_generator(), rate)),
21 isolate_(heap->isolate()),
22 heap_(heap),
23 random_(isolate_->random_number_generator()),
24 names_(names),
25 samples_(),
26 rate_(rate),
27 stack_depth_(stack_depth) {
28 heap->new_space()->AddInlineAllocationObserver(this);
29 }
30
31
32 SamplingHeapProfiler::~SamplingHeapProfiler() {
33 heap_->new_space()->RemoveInlineAllocationObserver(this);
34
35 // Clear samples and drop all the weak references we are keeping.
36 std::set<SampledAllocation*>::iterator it;
37 for (it = samples_.begin(); it != samples_.end(); ++it) {
38 delete *it;
39 }
40 std::set<SampledAllocation*> empty{};
41 samples_.swap(empty);
42 }
43
44 void SamplingHeapProfiler::Step(int bytes_allocated, Address soon_object,
45 size_t size) {
46 DCHECK(heap_->gc_state() == Heap::NOT_IN_GC);
47 DCHECK(soon_object);
48 SampleObject(soon_object, size);
49 }
50
51
52 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) {
53 DisallowHeapAllocation no_allocation;
54
55 HandleScope scope(isolate_);
56 HeapObject* heap_object = HeapObject::FromAddress(soon_object);
57 Handle<Object> obj(heap_object, isolate_);
58
59 // Mark the new block as FreeSpace to make sure the heap is iterable while we
60 // are taking the sample.
61 heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size));
62
63 Local<v8::Value> loc = v8::Utils::ToLocal(obj);
64
65 SampledAllocation* sample =
66 new SampledAllocation(this, isolate_, loc, size, stack_depth_);
67 samples_.insert(sample);
68 }
69
70
71 // We sample with a Poisson process, with constant average sampling interval.
72 // This follows the exponential probability distribution with parameter
73 // λ = 1/rate where rate is the average number of bytes between samples.
74 //
75 // Let u be a uniformly distributed random number between 0 and 1, then
76 // next_sample = (- ln u) / λ
77 intptr_t SamplingHeapProfiler::GetNextSampleInterval(
78 base::RandomNumberGenerator* random, uint64_t rate) {
79 double u = random->NextDouble();
80 double next = (-std::log(u)) * rate;
81 return std::max(static_cast<intptr_t>(kPointerSize),
82 static_cast<intptr_t>(next));
83 }
84
85
86 void SamplingHeapProfiler::SampledAllocation::OnWeakCallback(
87 const WeakCallbackInfo<SampledAllocation>& data) {
88 SampledAllocation* sample = data.GetParameter();
89 sample->shp_->samples_.erase(sample);
90 delete sample;
91 }
92
93
94 SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared,
95 StringsStorage* names)
96 : name_(names->GetFunctionName(shared->DebugName())),
97 script_name_(""),
98 script_id_(v8::UnboundScript::kNoScriptId),
99 start_position_(0) {
100 if (shared->script()->IsScript()) {
101 Script* script = Script::cast(shared->script());
102 script_id_ = script->id();
103 start_position_ = shared->start_position();
104 if (script->name()->IsName()) {
105 Name* name = Name::cast(script->name());
106 script_name_ = names->GetName(name);
107 }
108 }
109 }
110
111
112 SamplingHeapProfiler::SampledAllocation::SampledAllocation(
113 SamplingHeapProfiler* shp, Isolate* isolate, Local<Value> local,
114 size_t size, int max_frames)
115 : shp_(shp),
116 global_(reinterpret_cast<v8::Isolate*>(isolate), local),
117 size_(size) {
118 global_.SetWeak(this, OnWeakCallback, WeakCallbackType::kParameter);
119
120 StackTraceFrameIterator it(isolate);
121 int frames_captured = 0;
122 while (!it.done() && frames_captured < max_frames) {
123 JavaScriptFrame* frame = it.frame();
124 SharedFunctionInfo* shared = frame->function()->shared();
125 stack_.push_back(new FunctionInfo(shared, shp->names()));
126
127 frames_captured++;
128 it.Advance();
129 }
130
131 if (frames_captured == 0) {
132 const char* name = nullptr;
133 switch (isolate->current_vm_state()) {
134 case GC:
135 name = "(GC)";
136 break;
137 case COMPILER:
138 name = "(COMPILER)";
139 break;
140 case OTHER:
141 name = "(V8 API)";
142 break;
143 case EXTERNAL:
144 name = "(EXTERNAL)";
145 break;
146 case IDLE:
147 name = "(IDLE)";
148 break;
149 case JS:
150 name = "(JS)";
151 break;
152 }
153 stack_.push_back(
154 new FunctionInfo(name, "", v8::UnboundScript::kNoScriptId, 0));
155 }
156 }
157
158
159 SamplingHeapProfiler::Node* SamplingHeapProfiler::AllocateNode(
160 v8::AllocationProfile& profile, const std::map<int, Script*>& scripts,
161 FunctionInfo* function_info) {
162 DCHECK(function_info->get_name());
163 DCHECK(function_info->get_script_name());
164
165 int line = v8::AllocationProfile::kNoLineNumberInfo;
166 int column = v8::AllocationProfile::kNoColumnNumberInfo;
167
168 if (function_info->get_script_id() != v8::UnboundScript::kNoScriptId) {
169 Handle<Script> script(scripts.at(function_info->get_script_id()));
170
171 line =
172 1 + Script::GetLineNumber(script, function_info->get_start_position());
173 column = 1 + Script::GetColumnNumber(script,
174 function_info->get_start_position());
175 }
176
177 profile.nodes.push_back(
178 Node({ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String(
179 function_info->get_name())),
180 ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String(
181 function_info->get_script_name())),
182 function_info->get_script_id(), function_info->get_start_position(),
183 line, column, std::vector<Node*>(),
184 std::vector<v8::AllocationProfile::Allocation>()}));
185
186 return &profile.nodes.back();
187 }
188
189
190 SamplingHeapProfiler::Node* SamplingHeapProfiler::FindOrAddChildNode(
191 v8::AllocationProfile& profile, const std::map<int, Script*>& scripts,
192 Node* parent, FunctionInfo* function_info) {
193 for (Node* child : parent->children) {
194 if (child->script_id == function_info->get_script_id() &&
195 child->start_position == function_info->get_start_position())
196 return child;
197 }
198 Node* child = AllocateNode(profile, scripts, function_info);
199 parent->children.push_back(child);
200 return child;
201 }
202
203
204 SamplingHeapProfiler::Node* SamplingHeapProfiler::AddStack(
205 v8::AllocationProfile& profile, const std::map<int, Script*>& scripts,
206 const std::vector<FunctionInfo*>& stack) {
207 Node* node = profile.GetRootNode();
208
209 // We need to process the stack in reverse order as the top of the stack is
210 // the first element in the list.
211 for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
212 FunctionInfo* function_info = *it;
213 node = FindOrAddChildNode(profile, scripts, node, function_info);
214 }
215 return node;
216 }
217
218
219 v8::AllocationProfile SamplingHeapProfiler::GetAllocationProfile() {
220 // To resolve positions to line/column numbers, we will need to look up
221 // scripts. Build a map to allow fast mapping from script id to script.
222 std::map<int, Script*> scripts;
223 {
224 Script::Iterator iterator(isolate_);
225 Script* script;
226 while ((script = iterator.Next())) {
227 scripts[script->id()] = script;
228 }
229 }
230
231
232 v8::AllocationProfile profile;
233
234 // Create the root node.
235 FunctionInfo function_info("(root)", "", v8::UnboundScript::kNoScriptId, 0);
236 AllocateNode(profile, scripts, &function_info);
237
238 for (SampledAllocation* allocation : samples_) {
239 Node* node = AddStack(profile, scripts, allocation->get_stack());
240 node->allocations.push_back({allocation->get_size(), 1});
241 }
242
243 return profile;
244 }
245
246
247 } // namespace internal
248 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698