OLD | NEW |
---|---|
1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/profiler/sampling-heap-profiler.h" | 5 #include "src/profiler/sampling-heap-profiler.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 #include <memory> | 8 #include <memory> |
9 #include "src/api.h" | 9 #include "src/api.h" |
10 #include "src/base/utils/random-number-generator.h" | 10 #include "src/base/utils/random-number-generator.h" |
(...skipping 24 matching lines...) Expand all Loading... | |
35 | 35 |
36 SamplingHeapProfiler::SamplingHeapProfiler(Heap* heap, StringsStorage* names, | 36 SamplingHeapProfiler::SamplingHeapProfiler(Heap* heap, StringsStorage* names, |
37 uint64_t rate, int stack_depth) | 37 uint64_t rate, int stack_depth) |
38 : isolate_(heap->isolate()), | 38 : isolate_(heap->isolate()), |
39 heap_(heap), | 39 heap_(heap), |
40 new_space_observer_(new SamplingAllocationObserver( | 40 new_space_observer_(new SamplingAllocationObserver( |
41 heap_, rate, rate, this, heap->isolate()->random_number_generator())), | 41 heap_, rate, rate, this, heap->isolate()->random_number_generator())), |
42 other_spaces_observer_(new SamplingAllocationObserver( | 42 other_spaces_observer_(new SamplingAllocationObserver( |
43 heap_, rate, rate, this, heap->isolate()->random_number_generator())), | 43 heap_, rate, rate, this, heap->isolate()->random_number_generator())), |
44 names_(names), | 44 names_(names), |
45 profile_root_("(root)", v8::UnboundScript::kNoScriptId, 0), | |
45 samples_(), | 46 samples_(), |
46 stack_depth_(stack_depth) { | 47 stack_depth_(stack_depth) { |
47 heap->new_space()->AddAllocationObserver(new_space_observer_.get()); | 48 heap->new_space()->AddAllocationObserver(new_space_observer_.get()); |
48 AllSpaces spaces(heap); | 49 AllSpaces spaces(heap); |
49 for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { | 50 for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { |
50 if (space != heap->new_space()) { | 51 if (space != heap->new_space()) { |
51 space->AddAllocationObserver(other_spaces_observer_.get()); | 52 space->AddAllocationObserver(other_spaces_observer_.get()); |
52 } | 53 } |
53 } | 54 } |
54 } | 55 } |
55 | 56 |
56 | 57 |
57 SamplingHeapProfiler::~SamplingHeapProfiler() { | 58 SamplingHeapProfiler::~SamplingHeapProfiler() { |
58 heap_->new_space()->RemoveAllocationObserver(new_space_observer_.get()); | 59 heap_->new_space()->RemoveAllocationObserver(new_space_observer_.get()); |
59 AllSpaces spaces(heap_); | 60 AllSpaces spaces(heap_); |
60 for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { | 61 for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { |
61 if (space != heap_->new_space()) { | 62 if (space != heap_->new_space()) { |
62 space->RemoveAllocationObserver(other_spaces_observer_.get()); | 63 space->RemoveAllocationObserver(other_spaces_observer_.get()); |
63 } | 64 } |
64 } | 65 } |
65 | 66 |
66 // Clear samples and drop all the weak references we are keeping. | 67 for (auto sample : samples_) { |
67 std::set<SampledAllocation*>::iterator it; | 68 delete sample; |
68 for (it = samples_.begin(); it != samples_.end(); ++it) { | |
69 delete *it; | |
70 } | 69 } |
71 std::set<SampledAllocation*> empty; | 70 samples_.clear(); |
ofrobots
2016/02/18 01:02:24
nit: It is more efficient to do a swap with an emp
mattloring
2016/02/18 01:30:04
Done.
| |
72 samples_.swap(empty); | |
73 } | 71 } |
74 | 72 |
75 | 73 |
76 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) { | 74 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) { |
77 DisallowHeapAllocation no_allocation; | 75 DisallowHeapAllocation no_allocation; |
78 | 76 |
79 HandleScope scope(isolate_); | 77 HandleScope scope(isolate_); |
80 HeapObject* heap_object = HeapObject::FromAddress(soon_object); | 78 HeapObject* heap_object = HeapObject::FromAddress(soon_object); |
81 Handle<Object> obj(heap_object, isolate_); | 79 Handle<Object> obj(heap_object, isolate_); |
82 | 80 |
83 // Mark the new block as FreeSpace to make sure the heap is iterable while we | 81 // Mark the new block as FreeSpace to make sure the heap is iterable while we |
84 // are taking the sample. | 82 // are taking the sample. |
85 heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size)); | 83 heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size)); |
86 | 84 |
87 Local<v8::Value> loc = v8::Utils::ToLocal(obj); | 85 Local<v8::Value> loc = v8::Utils::ToLocal(obj); |
88 | 86 |
89 SampledAllocation* sample = | 87 AllocationNode* node = AddStack(); |
90 new SampledAllocation(this, isolate_, loc, size, stack_depth_); | 88 node->allocations_[size]++; |
89 Sample* sample = new Sample(size, node, loc, this); | |
91 samples_.insert(sample); | 90 samples_.insert(sample); |
91 sample->global.SetWeak(sample, OnWeakCallback, WeakCallbackType::kParameter); | |
92 } | 92 } |
93 | 93 |
94 | 94 void SamplingHeapProfiler::OnWeakCallback( |
95 void SamplingHeapProfiler::SampledAllocation::OnWeakCallback( | 95 const WeakCallbackInfo<Sample>& data) { |
96 const WeakCallbackInfo<SampledAllocation>& data) { | 96 Sample* sample = data.GetParameter(); |
97 SampledAllocation* sample = data.GetParameter(); | 97 AllocationNode* node = sample->owner; |
98 sample->sampling_heap_profiler_->samples_.erase(sample); | 98 node->allocations_[sample->size]--; |
ofrobots
2016/02/18 01:02:24
map::operator[] silently insert a new entry in the
mattloring
2016/02/18 01:30:04
Done.
| |
99 sample->profiler->samples_.erase(sample); | |
99 delete sample; | 100 delete sample; |
100 } | 101 } |
101 | 102 |
102 | 103 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::FindOrAddChildNode( |
103 SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared, | 104 AllocationNode* parent, const char* name, int script_id, |
104 StringsStorage* names) | 105 int start_position) { |
105 : name_(names->GetFunctionName(shared->DebugName())), | 106 for (AllocationNode* child : parent->children_) { |
106 script_name_(""), | 107 if (child->script_id_ == script_id && |
107 script_id_(v8::UnboundScript::kNoScriptId), | 108 child->script_position_ == start_position && |
108 start_position_(shared->start_position()) { | 109 strcmp(child->name_, name) == 0) { |
ofrobots
2016/02/18 01:02:24
I think the earlier comment from Ulan was to DCHEC
mattloring
2016/02/18 01:30:04
Actually, it is possible for two scripts to have t
| |
109 if (shared->script()->IsScript()) { | 110 return child; |
110 Script* script = Script::cast(shared->script()); | |
111 script_id_ = script->id(); | |
112 if (script->name()->IsName()) { | |
113 Name* name = Name::cast(script->name()); | |
114 script_name_ = names->GetName(name); | |
115 } | 111 } |
116 } | 112 } |
113 AllocationNode* child = new AllocationNode(name, script_id, start_position); | |
114 parent->children_.push_back(child); | |
115 return child; | |
117 } | 116 } |
118 | 117 |
118 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::AddStack() { | |
119 AllocationNode* node = &profile_root_; | |
119 | 120 |
120 SamplingHeapProfiler::SampledAllocation::SampledAllocation( | 121 std::vector<SharedFunctionInfo*> stack; |
121 SamplingHeapProfiler* sampling_heap_profiler, Isolate* isolate, | 122 StackTraceFrameIterator it(isolate_); |
122 Local<Value> local, size_t size, int max_frames) | |
123 : sampling_heap_profiler_(sampling_heap_profiler), | |
124 global_(reinterpret_cast<v8::Isolate*>(isolate), local), | |
125 size_(size) { | |
126 global_.SetWeak(this, OnWeakCallback, WeakCallbackType::kParameter); | |
127 | |
128 StackTraceFrameIterator it(isolate); | |
129 int frames_captured = 0; | 123 int frames_captured = 0; |
130 while (!it.done() && frames_captured < max_frames) { | 124 while (!it.done() && frames_captured < stack_depth_) { |
131 JavaScriptFrame* frame = it.frame(); | 125 JavaScriptFrame* frame = it.frame(); |
132 SharedFunctionInfo* shared = frame->function()->shared(); | 126 SharedFunctionInfo* shared = frame->function()->shared(); |
133 stack_.push_back(new FunctionInfo(shared, sampling_heap_profiler->names())); | 127 stack.push_back(shared); |
134 | 128 |
135 frames_captured++; | 129 frames_captured++; |
136 it.Advance(); | 130 it.Advance(); |
137 } | 131 } |
138 | 132 |
139 if (frames_captured == 0) { | 133 if (frames_captured == 0) { |
140 const char* name = nullptr; | 134 const char* name = nullptr; |
141 switch (isolate->current_vm_state()) { | 135 switch (isolate_->current_vm_state()) { |
142 case GC: | 136 case GC: |
143 name = "(GC)"; | 137 name = "(GC)"; |
144 break; | 138 break; |
145 case COMPILER: | 139 case COMPILER: |
146 name = "(COMPILER)"; | 140 name = "(COMPILER)"; |
147 break; | 141 break; |
148 case OTHER: | 142 case OTHER: |
149 name = "(V8 API)"; | 143 name = "(V8 API)"; |
150 break; | 144 break; |
151 case EXTERNAL: | 145 case EXTERNAL: |
152 name = "(EXTERNAL)"; | 146 name = "(EXTERNAL)"; |
153 break; | 147 break; |
154 case IDLE: | 148 case IDLE: |
155 name = "(IDLE)"; | 149 name = "(IDLE)"; |
156 break; | 150 break; |
157 case JS: | 151 case JS: |
158 name = "(JS)"; | 152 name = "(JS)"; |
159 break; | 153 break; |
160 } | 154 } |
161 stack_.push_back(new FunctionInfo(name)); | 155 return FindOrAddChildNode(node, name, v8::UnboundScript::kNoScriptId, 0); |
162 } | 156 } |
163 } | |
164 | |
165 v8::AllocationProfile::Node* SamplingHeapProfiler::AllocateNode( | |
166 AllocationProfile* profile, const std::map<int, Script*>& scripts, | |
167 FunctionInfo* function_info) { | |
168 DCHECK(function_info->get_name()); | |
169 DCHECK(function_info->get_script_name()); | |
170 | |
171 int line = v8::AllocationProfile::kNoLineNumberInfo; | |
172 int column = v8::AllocationProfile::kNoColumnNumberInfo; | |
173 | |
174 if (function_info->get_script_id() != v8::UnboundScript::kNoScriptId) { | |
175 // Cannot use std::map<T>::at because it is not available on android. | |
176 auto non_const_scripts = const_cast<std::map<int, Script*>&>(scripts); | |
177 Handle<Script> script(non_const_scripts[function_info->get_script_id()]); | |
178 | |
179 line = | |
180 1 + Script::GetLineNumber(script, function_info->get_start_position()); | |
181 column = 1 + Script::GetColumnNumber(script, | |
182 function_info->get_start_position()); | |
183 } | |
184 | |
185 profile->nodes().push_back(v8::AllocationProfile::Node( | |
186 {ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String( | |
187 function_info->get_name())), | |
188 ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String( | |
189 function_info->get_script_name())), | |
190 function_info->get_script_id(), function_info->get_start_position(), | |
191 line, column, std::vector<v8::AllocationProfile::Node*>(), | |
192 std::vector<v8::AllocationProfile::Allocation>()})); | |
193 | |
194 return &profile->nodes().back(); | |
195 } | |
196 | |
197 v8::AllocationProfile::Node* SamplingHeapProfiler::FindOrAddChildNode( | |
198 AllocationProfile* profile, const std::map<int, Script*>& scripts, | |
199 v8::AllocationProfile::Node* parent, FunctionInfo* function_info) { | |
200 for (v8::AllocationProfile::Node* child : parent->children) { | |
201 if (child->script_id == function_info->get_script_id() && | |
202 child->start_position == function_info->get_start_position()) | |
203 return child; | |
204 } | |
205 v8::AllocationProfile::Node* child = | |
206 AllocateNode(profile, scripts, function_info); | |
207 parent->children.push_back(child); | |
208 return child; | |
209 } | |
210 | |
211 v8::AllocationProfile::Node* SamplingHeapProfiler::AddStack( | |
212 AllocationProfile* profile, const std::map<int, Script*>& scripts, | |
213 const std::vector<FunctionInfo*>& stack) { | |
214 v8::AllocationProfile::Node* node = profile->GetRootNode(); | |
215 | 157 |
216 // We need to process the stack in reverse order as the top of the stack is | 158 // We need to process the stack in reverse order as the top of the stack is |
217 // the first element in the list. | 159 // the first element in the list. |
218 for (auto it = stack.rbegin(); it != stack.rend(); ++it) { | 160 for (auto it = stack.rbegin(); it != stack.rend(); ++it) { |
219 FunctionInfo* function_info = *it; | 161 SharedFunctionInfo* shared = *it; |
220 node = FindOrAddChildNode(profile, scripts, node, function_info); | 162 const char* name = this->names()->GetFunctionName(shared->DebugName()); |
163 int script_id = v8::UnboundScript::kNoScriptId; | |
164 if (shared->script()->IsScript()) { | |
165 Script* script = Script::cast(shared->script()); | |
166 script_id = script->id(); | |
167 } | |
168 node = FindOrAddChildNode(node, name, script_id, shared->start_position()); | |
221 } | 169 } |
222 return node; | 170 return node; |
223 } | 171 } |
224 | 172 |
173 v8::AllocationProfile::Node* SamplingHeapProfiler::TranslateAllocationNode( | |
174 AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node, | |
175 const std::map<int, Script*>& scripts) { | |
176 Local<v8::String> script_name = | |
177 ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String("")); | |
178 int line = v8::AllocationProfile::kNoLineNumberInfo; | |
179 int column = v8::AllocationProfile::kNoColumnNumberInfo; | |
180 std::vector<v8::AllocationProfile::Allocation> allocations; | |
181 if (node->script_id_ != v8::UnboundScript::kNoScriptId) { | |
182 // Cannot use std::map<T>::at because it is not available on android. | |
183 auto non_const_scripts = const_cast<std::map<int, Script*>&>(scripts); | |
184 Script* script = non_const_scripts[node->script_id_]; | |
185 if (script->name()->IsName()) { | |
186 Name* name = Name::cast(script->name()); | |
187 script_name = ToApiHandle<v8::String>( | |
188 isolate_->factory()->InternalizeUtf8String(names_->GetName(name))); | |
189 } | |
190 Handle<Script> script_handle(script); | |
191 | |
192 line = 1 + Script::GetLineNumber(script_handle, node->script_position_); | |
193 column = 1 + Script::GetColumnNumber(script_handle, node->script_position_); | |
194 for (auto alloc : node->allocations_) { | |
195 allocations.push_back({alloc.first, alloc.second}); | |
196 } | |
197 } | |
198 | |
199 profile->nodes().push_back(v8::AllocationProfile::Node( | |
200 {ToApiHandle<v8::String>( | |
201 isolate_->factory()->InternalizeUtf8String(node->name_)), | |
202 script_name, node->script_id_, node->script_position_, line, column, | |
203 std::vector<v8::AllocationProfile::Node*>(), allocations})); | |
204 v8::AllocationProfile::Node* current = &profile->nodes().back(); | |
205 for (auto child : node->children_) { | |
206 current->children.push_back( | |
207 TranslateAllocationNode(profile, child, scripts)); | |
208 } | |
209 return current; | |
210 } | |
225 | 211 |
226 v8::AllocationProfile* SamplingHeapProfiler::GetAllocationProfile() { | 212 v8::AllocationProfile* SamplingHeapProfiler::GetAllocationProfile() { |
227 // To resolve positions to line/column numbers, we will need to look up | 213 // To resolve positions to line/column numbers, we will need to look up |
228 // scripts. Build a map to allow fast mapping from script id to script. | 214 // scripts. Build a map to allow fast mapping from script id to script. |
229 std::map<int, Script*> scripts; | 215 std::map<int, Script*> scripts; |
230 { | 216 { |
231 Script::Iterator iterator(isolate_); | 217 Script::Iterator iterator(isolate_); |
232 Script* script; | 218 Script* script; |
233 while ((script = iterator.Next())) { | 219 while ((script = iterator.Next())) { |
234 scripts[script->id()] = script; | 220 scripts[script->id()] = script; |
235 } | 221 } |
236 } | 222 } |
237 | 223 |
238 auto profile = new v8::internal::AllocationProfile(); | 224 auto profile = new v8::internal::AllocationProfile(); |
239 | 225 |
240 // Create the root node. | 226 TranslateAllocationNode(profile, &profile_root_, scripts); |
241 FunctionInfo function_info("(root)"); | |
242 AllocateNode(profile, scripts, &function_info); | |
243 | |
244 for (SampledAllocation* allocation : samples_) { | |
245 v8::AllocationProfile::Node* node = | |
246 AddStack(profile, scripts, allocation->get_stack()); | |
247 node->allocations.push_back({allocation->get_size(), 1}); | |
248 } | |
249 | 227 |
250 return profile; | 228 return profile; |
251 } | 229 } |
252 | 230 |
253 | 231 |
254 } // namespace internal | 232 } // namespace internal |
255 } // namespace v8 | 233 } // namespace v8 |
OLD | NEW |