Chromium Code Reviews| 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 samples_(), | |
| 46 stack_depth_(stack_depth) { | 45 stack_depth_(stack_depth) { |
| 46 FunctionInfo function_info("(root)"); | |
| 47 profile_root_ = new SamplingHeapProfiler::AllocationNode(&function_info); | |
|
ofrobots
2016/02/14 23:57:42
drop the SamplingHeapProfiler:: namespace prefix h
mattloring
2016/02/16 05:28:40
Done.
| |
| 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 delete profile_root_; |
| 67 std::set<SampledAllocation*>::iterator it; | |
| 68 for (it = samples_.begin(); it != samples_.end(); ++it) { | |
| 69 delete *it; | |
| 70 } | |
|
ofrobots
2016/02/14 23:57:42
Are we still clearing the weak references we have
mattloring
2016/02/16 05:28:40
Globals should be cleaned up now.
| |
| 71 std::set<SampledAllocation*> empty; | |
| 72 samples_.swap(empty); | |
| 73 } | 68 } |
| 74 | 69 |
| 75 | 70 |
| 76 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) { | 71 void SamplingHeapProfiler::SampleObject(Address soon_object, size_t size) { |
| 77 DisallowHeapAllocation no_allocation; | 72 DisallowHeapAllocation no_allocation; |
| 78 | 73 |
| 79 HandleScope scope(isolate_); | 74 HandleScope scope(isolate_); |
| 80 HeapObject* heap_object = HeapObject::FromAddress(soon_object); | 75 HeapObject* heap_object = HeapObject::FromAddress(soon_object); |
| 81 Handle<Object> obj(heap_object, isolate_); | 76 Handle<Object> obj(heap_object, isolate_); |
| 82 | 77 |
| 83 // Mark the new block as FreeSpace to make sure the heap is iterable while we | 78 // Mark the new block as FreeSpace to make sure the heap is iterable while we |
| 84 // are taking the sample. | 79 // are taking the sample. |
| 85 heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size)); | 80 heap()->CreateFillerObjectAt(soon_object, static_cast<int>(size)); |
| 86 | 81 |
| 87 Local<v8::Value> loc = v8::Utils::ToLocal(obj); | 82 Local<v8::Value> loc = v8::Utils::ToLocal(obj); |
| 83 Global<Value>* global = | |
| 84 new Global<Value>(reinterpret_cast<v8::Isolate*>(isolate_), loc); | |
| 88 | 85 |
| 89 SampledAllocation* sample = | 86 std::vector<FunctionInfo*> stack = CollectStack(stack_depth_); |
| 90 new SampledAllocation(this, isolate_, loc, size, stack_depth_); | 87 SamplingHeapProfiler::AllocationNode* node = AddStack(stack); |
| 91 samples_.insert(sample); | 88 node->allocations_[size]++; |
| 89 Sample* sample = new Sample({size, node, global}); | |
|
ofrobots
2016/02/14 23:57:42
Prefereable to avoid list initialization. Prefer e
mattloring
2016/02/16 05:28:40
Doing it as {...} yields: error: excess elements i
ofrobots
2016/02/17 17:48:21
You will need to use a constructor that accepts th
mattloring
2016/02/17 17:59:47
Done.
| |
| 90 | |
| 91 global->SetWeak(sample, OnWeakCallback, WeakCallbackType::kParameter); | |
| 92 } | 92 } |
| 93 | 93 |
| 94 | 94 std::vector<SamplingHeapProfiler::FunctionInfo*> |
| 95 void SamplingHeapProfiler::SampledAllocation::OnWeakCallback( | 95 SamplingHeapProfiler::CollectStack(int max_frames) { |
| 96 const WeakCallbackInfo<SampledAllocation>& data) { | 96 std::vector<SamplingHeapProfiler::FunctionInfo*> stack_; |
| 97 SampledAllocation* sample = data.GetParameter(); | 97 StackTraceFrameIterator it(isolate_); |
| 98 sample->sampling_heap_profiler_->samples_.erase(sample); | |
| 99 delete sample; | |
| 100 } | |
| 101 | |
| 102 | |
| 103 SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared, | |
| 104 StringsStorage* names) | |
| 105 : name_(names->GetFunctionName(shared->DebugName())), | |
| 106 script_name_(""), | |
| 107 script_id_(v8::UnboundScript::kNoScriptId), | |
| 108 start_position_(shared->start_position()) { | |
| 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); | |
| 115 } | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 | |
| 120 SamplingHeapProfiler::SampledAllocation::SampledAllocation( | |
| 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); | |
| 129 int frames_captured = 0; | 98 int frames_captured = 0; |
| 130 while (!it.done() && frames_captured < max_frames) { | 99 while (!it.done() && frames_captured < max_frames) { |
| 131 JavaScriptFrame* frame = it.frame(); | 100 JavaScriptFrame* frame = it.frame(); |
| 132 SharedFunctionInfo* shared = frame->function()->shared(); | 101 SharedFunctionInfo* shared = frame->function()->shared(); |
| 133 stack_.push_back(new FunctionInfo(shared, sampling_heap_profiler->names())); | 102 stack_.push_back(new FunctionInfo(shared, this->names())); |
| 134 | 103 |
| 135 frames_captured++; | 104 frames_captured++; |
| 136 it.Advance(); | 105 it.Advance(); |
| 137 } | 106 } |
| 138 | 107 |
| 139 if (frames_captured == 0) { | 108 if (frames_captured == 0) { |
| 140 const char* name = nullptr; | 109 const char* name = nullptr; |
| 141 switch (isolate->current_vm_state()) { | 110 switch (isolate_->current_vm_state()) { |
| 142 case GC: | 111 case GC: |
| 143 name = "(GC)"; | 112 name = "(GC)"; |
| 144 break; | 113 break; |
| 145 case COMPILER: | 114 case COMPILER: |
| 146 name = "(COMPILER)"; | 115 name = "(COMPILER)"; |
| 147 break; | 116 break; |
| 148 case OTHER: | 117 case OTHER: |
| 149 name = "(V8 API)"; | 118 name = "(V8 API)"; |
| 150 break; | 119 break; |
| 151 case EXTERNAL: | 120 case EXTERNAL: |
| 152 name = "(EXTERNAL)"; | 121 name = "(EXTERNAL)"; |
| 153 break; | 122 break; |
| 154 case IDLE: | 123 case IDLE: |
| 155 name = "(IDLE)"; | 124 name = "(IDLE)"; |
| 156 break; | 125 break; |
| 157 case JS: | 126 case JS: |
| 158 name = "(JS)"; | 127 name = "(JS)"; |
| 159 break; | 128 break; |
| 160 } | 129 } |
| 161 stack_.push_back(new FunctionInfo(name)); | 130 stack_.push_back(new FunctionInfo(name)); |
| 162 } | 131 } |
| 132 return stack_; | |
| 163 } | 133 } |
| 164 | 134 |
| 165 v8::AllocationProfile::Node* SamplingHeapProfiler::AllocateNode( | 135 void SamplingHeapProfiler::OnWeakCallback( |
| 166 AllocationProfile* profile, const std::map<int, Script*>& scripts, | 136 const WeakCallbackInfo<SamplingHeapProfiler::Sample>& data) { |
|
ofrobots
2016/02/14 23:57:42
You might be able to drop the SHP:: prefix in the
mattloring
2016/02/16 05:28:39
Done.
| |
| 167 FunctionInfo* function_info) { | 137 SamplingHeapProfiler::Sample* sample = data.GetParameter(); |
| 168 DCHECK(function_info->get_name()); | 138 SamplingHeapProfiler::AllocationNode* node = sample->owner; |
|
ofrobots
2016/02/14 23:57:42
Drop the SHP:: prefix.
mattloring
2016/02/16 05:28:40
Done.
| |
| 169 DCHECK(function_info->get_script_name()); | 139 node->allocations_[sample->size]--; |
| 170 | 140 delete sample->global; |
| 171 int line = v8::AllocationProfile::kNoLineNumberInfo; | 141 delete sample; |
| 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 } | 142 } |
| 196 | 143 |
| 197 v8::AllocationProfile::Node* SamplingHeapProfiler::FindOrAddChildNode( | 144 SamplingHeapProfiler::FunctionInfo::FunctionInfo(SharedFunctionInfo* shared, |
| 198 AllocationProfile* profile, const std::map<int, Script*>& scripts, | 145 StringsStorage* names) |
| 199 v8::AllocationProfile::Node* parent, FunctionInfo* function_info) { | 146 : name_(names->GetFunctionName(shared->DebugName())), |
| 200 for (v8::AllocationProfile::Node* child : parent->children) { | 147 script_name_(""), |
|
ofrobots
2016/02/14 23:57:42
Do we ever use this field any more? Drop it if you
mattloring
2016/02/16 15:10:54
Done.
| |
| 201 if (child->script_id == function_info->get_script_id() && | 148 script_id_(v8::UnboundScript::kNoScriptId), |
| 202 child->start_position == function_info->get_start_position()) | 149 start_position_(shared->start_position()) { |
| 150 if (shared->script()->IsScript()) { | |
| 151 Script* script = Script::cast(shared->script()); | |
| 152 script_id_ = script->id(); | |
| 153 if (script->name()->IsName()) { | |
| 154 Name* name = Name::cast(script->name()); | |
| 155 script_name_ = names->GetName(name); | |
| 156 } | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::FindOrAddChildNode( | |
| 161 SamplingHeapProfiler::AllocationNode* parent, FunctionInfo* function_info) { | |
| 162 for (SamplingHeapProfiler::AllocationNode* child : parent->children_) { | |
|
ofrobots
2016/02/14 23:57:42
drop the prefix.
mattloring
2016/02/16 05:28:39
Done.
| |
| 163 if (child->script_id_ == function_info->get_script_id() && | |
| 164 child->script_position_ == function_info->get_start_position()) | |
| 203 return child; | 165 return child; |
| 204 } | 166 } |
| 205 v8::AllocationProfile::Node* child = | 167 SamplingHeapProfiler::AllocationNode* child = |
|
ofrobots
2016/02/14 23:57:42
Drop the SamplingHeapProfiler prefix.
mattloring
2016/02/16 05:28:40
Done.
| |
| 206 AllocateNode(profile, scripts, function_info); | 168 new AllocationNode(function_info); |
| 207 parent->children.push_back(child); | 169 parent->children_.push_back(child); |
| 208 return child; | 170 return child; |
| 209 } | 171 } |
| 210 | 172 |
| 211 v8::AllocationProfile::Node* SamplingHeapProfiler::AddStack( | 173 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::AddStack( |
| 212 AllocationProfile* profile, const std::map<int, Script*>& scripts, | |
| 213 const std::vector<FunctionInfo*>& stack) { | 174 const std::vector<FunctionInfo*>& stack) { |
| 214 v8::AllocationProfile::Node* node = profile->GetRootNode(); | 175 SamplingHeapProfiler::AllocationNode* node = profile_root_; |
|
ofrobots
2016/02/14 23:57:42
drop the prefix.
mattloring
2016/02/16 05:28:39
Done.
| |
| 215 | 176 |
| 216 // We need to process the stack in reverse order as the top of the stack is | 177 // We need to process the stack in reverse order as the top of the stack is |
| 217 // the first element in the list. | 178 // the first element in the list. |
| 218 for (auto it = stack.rbegin(); it != stack.rend(); ++it) { | 179 for (auto it = stack.rbegin(); it != stack.rend(); ++it) { |
| 219 FunctionInfo* function_info = *it; | 180 FunctionInfo* function_info = *it; |
| 220 node = FindOrAddChildNode(profile, scripts, node, function_info); | 181 node = FindOrAddChildNode(node, function_info); |
| 221 } | 182 } |
| 222 return node; | 183 return node; |
| 223 } | 184 } |
| 224 | 185 |
| 186 v8::AllocationProfile::Node* SamplingHeapProfiler::GenerateProfile( | |
| 187 AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node, | |
| 188 const std::map<int, Script*>& scripts) { | |
| 189 Local<v8::String> script_name = | |
| 190 ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String("")); | |
| 191 int line = v8::AllocationProfile::kNoLineNumberInfo; | |
| 192 int column = v8::AllocationProfile::kNoColumnNumberInfo; | |
| 193 std::vector<v8::AllocationProfile::Allocation> allocations; | |
|
ofrobots
2016/02/14 23:57:42
In a follow-on CL, it would be good to make the V8
mattloring
2016/02/16 05:28:40
Yup, this change was in the original version of th
| |
| 194 if (node->script_id_ != v8::UnboundScript::kNoScriptId) { | |
| 195 auto non_const_scripts = const_cast<std::map<int, Script*>&>(scripts); | |
|
ofrobots
2016/02/14 23:57:42
You dropped the necessary comment explaining why t
| |
| 196 Script* script = non_const_scripts[node->script_id_]; | |
| 197 if (script->name()->IsName()) { | |
| 198 Name* name = Name::cast(script->name()); | |
| 199 script_name = ToApiHandle<v8::String>( | |
| 200 isolate_->factory()->InternalizeUtf8String(names_->GetName(name))); | |
| 201 } | |
| 202 Handle<Script> script_handle(script); | |
| 203 | |
| 204 line = 1 + Script::GetLineNumber(script_handle, node->script_position_); | |
| 205 column = 1 + Script::GetColumnNumber(script_handle, node->script_position_); | |
| 206 for (auto alloc : node->allocations_) { | |
| 207 allocations.push_back({alloc.first, alloc.second}); | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 profile->nodes().push_back(v8::AllocationProfile::Node( | |
| 212 {ToApiHandle<v8::String>( | |
| 213 isolate_->factory()->InternalizeUtf8String(node->name_)), | |
| 214 script_name, node->script_id_, node->script_position_, line, column, | |
| 215 std::vector<v8::AllocationProfile::Node*>(), allocations})); | |
| 216 v8::AllocationProfile::Node* current = &profile->nodes().back(); | |
| 217 for (auto child : node->children_) { | |
| 218 current->children.push_back(GenerateProfile(profile, child, scripts)); | |
| 219 } | |
| 220 return current; | |
| 221 } | |
| 225 | 222 |
| 226 v8::AllocationProfile* SamplingHeapProfiler::GetAllocationProfile() { | 223 v8::AllocationProfile* SamplingHeapProfiler::GetAllocationProfile() { |
| 227 // To resolve positions to line/column numbers, we will need to look up | 224 // 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. | 225 // scripts. Build a map to allow fast mapping from script id to script. |
| 229 std::map<int, Script*> scripts; | 226 std::map<int, Script*> scripts; |
| 230 { | 227 { |
| 231 Script::Iterator iterator(isolate_); | 228 Script::Iterator iterator(isolate_); |
| 232 Script* script; | 229 Script* script; |
| 233 while ((script = iterator.Next())) { | 230 while ((script = iterator.Next())) { |
| 234 scripts[script->id()] = script; | 231 scripts[script->id()] = script; |
| 235 } | 232 } |
| 236 } | 233 } |
| 237 | 234 |
| 238 auto profile = new v8::internal::AllocationProfile(); | 235 auto profile = new v8::internal::AllocationProfile(); |
| 239 | 236 |
| 240 // Create the root node. | 237 GenerateProfile(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 | 238 |
| 250 return profile; | 239 return profile; |
| 251 } | 240 } |
| 252 | 241 |
| 253 | 242 |
| 254 } // namespace internal | 243 } // namespace internal |
| 255 } // namespace v8 | 244 } // namespace v8 |
| OLD | NEW |