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