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

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: increase sampling frequency in tests to reduce random chance failure likelihood 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 <stdint.h>
8 #include <memory>
9 #include "src/api.h"
10 #include "src/frames-inl.h"
11 #include "src/heap/heap.h"
12 #include "src/isolate.h"
13 #include "src/profiler/strings-storage.h"
14
15 namespace v8 {
16 namespace internal {
17
18 SamplingHeapProfiler::SamplingHeapProfiler(Heap* heap, StringsStorage* names,
19 uint64_t rate, int stack_depth)
20 : InlineAllocationObserver(GetNextSampleInterval(
21 heap->isolate()->random_number_generator(), rate)),
22 isolate_(heap->isolate()),
23 heap_(heap),
24 random_(isolate_->random_number_generator()),
25 names_(names),
26 samples_(),
27 rate_(rate),
28 stack_depth_(stack_depth) {
29 heap->new_space()->AddInlineAllocationObserver(this);
30 }
31
32
33 SamplingHeapProfiler::~SamplingHeapProfiler() {
34 heap_->new_space()->RemoveInlineAllocationObserver(this);
35
36 // Clear samples and drop all the weak references we are keeping.
37 std::set<SampledAllocation*>::iterator it;
38 for (it = samples_.begin(); it != samples_.end(); ++it) {
39 delete *it;
40 }
41 std::set<SampledAllocation*> empty;
42 samples_.swap(empty);
43 }
44
45 void SamplingHeapProfiler::Step(int bytes_allocated, Address soon_object,
46 size_t size) {
47 DCHECK(heap_->gc_state() == Heap::NOT_IN_GC);
48 DCHECK(soon_object);
49 SampleObject(soon_object, size);
50 }
51
52
53 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) {
54 DisallowHeapAllocation no_allocation;
55
56 HandleScope scope(isolate_);
57 HeapObject* heap_object = HeapObject::FromAddress(soon_object);
58 Handle<Object> obj(heap_object, isolate_);
59
60 // Mark the new block as FreeSpace to make sure the heap is iterable while we
61 // are taking the sample.
62 heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size));
63
64 Local<v8::Value> loc = v8::Utils::ToLocal(obj);
65
66 SampledAllocation* sample =
67 new SampledAllocation(this, isolate_, loc, size, stack_depth_);
68 samples_.insert(sample);
69 }
70
71
72 // We sample with a Poisson process, with constant average sampling interval.
73 // This follows the exponential probability distribution with parameter
74 // λ = 1/rate where rate is the average number of bytes between samples.
75 //
76 // Let u be a uniformly distributed random number between 0 and 1, then
77 // next_sample = (- ln u) / λ
78 intptr_t SamplingHeapProfiler::GetNextSampleInterval(
79 base::RandomNumberGenerator* random, uint64_t rate) {
80 double u = random->NextDouble();
81 double next = (-std::log(u)) * rate;
82 return next < kPointerSize
83 ? kPointerSize
84 : (next > INTPTR_MAX ? INTPTR_MAX : static_cast<intptr_t>(next));
alph 2016/01/20 23:13:01 I have a little concern here. The Poisson distribu
ofrobots 2016/01/21 03:03:28 The probability of `next` being close to INTPTR_MA
alph 2016/01/21 03:21:55 Well, the probability of getting a weird number is
85 }
86
87
88 void SamplingHeapProfiler::SampledAllocation::OnWeakCallback(
89 const WeakCallbackInfo<SampledAllocation>& data) {
90 SampledAllocation* sample = data.GetParameter();
91 sample->sampling_heap_profiler_->samples_.erase(sample);
92 delete sample;
93 }
94
95
96 SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared,
97 StringsStorage* names)
98 : name_(names->GetFunctionName(shared->DebugName())),
99 script_name_(""),
100 script_id_(v8::UnboundScript::kNoScriptId),
101 start_position_(0) {
102 if (shared->script()->IsScript()) {
103 Script* script = Script::cast(shared->script());
104 script_id_ = script->id();
105 start_position_ = shared->start_position();
alph 2016/01/20 23:13:01 Can you always set start_position_?
ofrobots 2016/01/21 03:03:28 Done.
106 if (script->name()->IsName()) {
107 Name* name = Name::cast(script->name());
108 script_name_ = names->GetName(name);
109 }
110 }
111 }
112
113
114 SamplingHeapProfiler::SampledAllocation::SampledAllocation(
115 SamplingHeapProfiler* sampling_heap_profiler, Isolate* isolate,
116 Local<Value> local, size_t size, int max_frames)
117 : sampling_heap_profiler_(sampling_heap_profiler),
118 global_(reinterpret_cast<v8::Isolate*>(isolate), local),
119 size_(size) {
120 global_.SetWeak(this, OnWeakCallback, WeakCallbackType::kParameter);
121
122 StackTraceFrameIterator it(isolate);
123 int frames_captured = 0;
124 while (!it.done() && frames_captured < max_frames) {
125 JavaScriptFrame* frame = it.frame();
126 SharedFunctionInfo* shared = frame->function()->shared();
127 stack_.push_back(new FunctionInfo(shared, sampling_heap_profiler->names()));
128
129 frames_captured++;
130 it.Advance();
131 }
132
133 if (frames_captured == 0) {
134 const char* name = nullptr;
135 switch (isolate->current_vm_state()) {
alph 2016/01/20 23:13:01 Could you reuse StateToString here? https://code.g
ofrobots 2016/01/21 03:03:28 The format returned by that isn't great for this p
136 case GC:
137 name = "(GC)";
138 break;
139 case COMPILER:
140 name = "(COMPILER)";
141 break;
142 case OTHER:
143 name = "(V8 API)";
144 break;
145 case EXTERNAL:
146 name = "(EXTERNAL)";
147 break;
148 case IDLE:
149 name = "(IDLE)";
150 break;
151 case JS:
152 name = "(JS)";
153 break;
154 }
155 stack_.push_back(
156 new FunctionInfo(name, "", v8::UnboundScript::kNoScriptId, 0));
157 }
158 }
159
160
161 SamplingHeapProfiler::Node* SamplingHeapProfiler::AllocateNode(
162 AllocationProfile& profile, const std::map<int, Script*>& scripts,
163 FunctionInfo* function_info) {
164 DCHECK(function_info->get_name());
165 DCHECK(function_info->get_script_name());
166
167 int line = v8::AllocationProfile::kNoLineNumberInfo;
168 int column = v8::AllocationProfile::kNoColumnNumberInfo;
169
170 if (function_info->get_script_id() != v8::UnboundScript::kNoScriptId) {
171 // Cannot use std::map<T>::at because it is not available on android.
172 auto non_const_scripts = const_cast<std::map<int, Script*>&>(scripts);
173 Handle<Script> script(non_const_scripts[function_info->get_script_id()]);
174
175 line =
176 1 + Script::GetLineNumber(script, function_info->get_start_position());
177 column = 1 + Script::GetColumnNumber(script,
178 function_info->get_start_position());
179 }
180
181 profile.nodes().push_back(
182 Node(ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String(
183 function_info->get_name())),
184 ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String(
185 function_info->get_script_name())),
186 function_info->get_script_id(), function_info->get_start_position(),
187 line, column));
188
189 return &profile.nodes().back();
190 }
191
192
193 SamplingHeapProfiler::Node* SamplingHeapProfiler::FindOrAddChildNode(
194 AllocationProfile& profile, const std::map<int, Script*>& scripts,
195 Node* parent, FunctionInfo* function_info) {
196 for (Node* child : parent->children) {
197 if (child->script_id == function_info->get_script_id() &&
alph 2016/01/20 23:13:01 Makes sense to use a map for children.
ofrobots 2016/01/21 03:03:28 I agree that this would be a nice optimization in
alph 2016/01/21 03:21:55 It affect the API, so if you want to change it it'
198 child->start_position == function_info->get_start_position())
199 return child;
200 }
201 Node* child = AllocateNode(profile, scripts, function_info);
202 parent->children.push_back(child);
203 return child;
204 }
205
206
207 SamplingHeapProfiler::Node* SamplingHeapProfiler::AddStack(
208 AllocationProfile& profile, const std::map<int, Script*>& scripts,
209 const std::vector<FunctionInfo*>& stack) {
210 Node* node = profile.GetRootNode();
211
212 // We need to process the stack in reverse order as the top of the stack is
213 // the first element in the list.
214 for (auto it = stack.rbegin(); it != stack.rend(); ++it) {
215 FunctionInfo* function_info = *it;
216 node = FindOrAddChildNode(profile, scripts, node, function_info);
217 }
218 return node;
219 }
220
221
222 v8::AllocationProfile* SamplingHeapProfiler::GetAllocationProfile() {
223 // To resolve positions to line/column numbers, we will need to look up
224 // scripts. Build a map to allow fast mapping from script id to script.
225 std::map<int, Script*> scripts;
226 {
227 Script::Iterator iterator(isolate_);
228 Script* script;
229 while ((script = iterator.Next())) {
230 scripts[script->id()] = script;
231 }
232 }
233
234
alph 2016/01/20 23:13:01 kill extra line
ofrobots 2016/01/21 03:03:28 Done.
235 auto profile = new v8::internal::AllocationProfile();
236
237 // Create the root node.
238 FunctionInfo function_info("(root)", "", v8::UnboundScript::kNoScriptId, 0);
alph 2016/01/20 23:13:01 Seems to be the last three parameters are never us
ofrobots 2016/01/21 03:03:28 Done.
239 AllocateNode(*profile, scripts, &function_info);
alph 2016/01/20 23:13:01 profile is an out parameter. Makes sense to pass i
ofrobots 2016/01/21 03:03:28 Done.
240
241 for (SampledAllocation* allocation : samples_) {
242 Node* node = AddStack(*profile, scripts, allocation->get_stack());
243 node->allocations.push_back({allocation->get_size(), 1});
244 }
245
246 return profile;
247 }
248
249
250 } // namespace internal
251 } // namespace v8
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698