| 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 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 51 uint64_t rate, int stack_depth) | 51 uint64_t rate, int stack_depth) |
| 52 : isolate_(heap->isolate()), | 52 : isolate_(heap->isolate()), |
| 53 heap_(heap), | 53 heap_(heap), |
| 54 new_space_observer_(new SamplingAllocationObserver( | 54 new_space_observer_(new SamplingAllocationObserver( |
| 55 heap_, static_cast<intptr_t>(rate), rate, this, | 55 heap_, static_cast<intptr_t>(rate), rate, this, |
| 56 heap->isolate()->random_number_generator())), | 56 heap->isolate()->random_number_generator())), |
| 57 other_spaces_observer_(new SamplingAllocationObserver( | 57 other_spaces_observer_(new SamplingAllocationObserver( |
| 58 heap_, static_cast<intptr_t>(rate), rate, this, | 58 heap_, static_cast<intptr_t>(rate), rate, this, |
| 59 heap->isolate()->random_number_generator())), | 59 heap->isolate()->random_number_generator())), |
| 60 names_(names), | 60 names_(names), |
| 61 profile_root_("(root)", v8::UnboundScript::kNoScriptId, 0), | 61 profile_root_(nullptr, "(root)", v8::UnboundScript::kNoScriptId, 0), |
| 62 samples_(), | 62 samples_(), |
| 63 stack_depth_(stack_depth), | 63 stack_depth_(stack_depth), |
| 64 rate_(rate) { | 64 rate_(rate) { |
| 65 CHECK_GT(rate_, 0); | 65 CHECK_GT(rate_, 0); |
| 66 heap->new_space()->AddAllocationObserver(new_space_observer_.get()); | 66 heap->new_space()->AddAllocationObserver(new_space_observer_.get()); |
| 67 AllSpaces spaces(heap); | 67 AllSpaces spaces(heap); |
| 68 for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { | 68 for (Space* space = spaces.next(); space != nullptr; space = spaces.next()) { |
| 69 if (space != heap->new_space()) { | 69 if (space != heap->new_space()) { |
| 70 space->AddAllocationObserver(other_spaces_observer_.get()); | 70 space->AddAllocationObserver(other_spaces_observer_.get()); |
| 71 } | 71 } |
| 72 } | 72 } |
| 73 } | 73 } |
| 74 | 74 |
| 75 | 75 |
| 76 SamplingHeapProfiler::~SamplingHeapProfiler() { | 76 SamplingHeapProfiler::~SamplingHeapProfiler() { |
| 77 heap_->new_space()->RemoveAllocationObserver(new_space_observer_.get()); | 77 heap_->new_space()->RemoveAllocationObserver(new_space_observer_.get()); |
| 78 AllSpaces spaces(heap_); | 78 AllSpaces spaces(heap_); |
| 79 for (Space* space = spaces.next(); space != NULL; space = spaces.next()) { | 79 for (Space* space = spaces.next(); space != nullptr; space = spaces.next()) { |
| 80 if (space != heap_->new_space()) { | 80 if (space != heap_->new_space()) { |
| 81 space->RemoveAllocationObserver(other_spaces_observer_.get()); | 81 space->RemoveAllocationObserver(other_spaces_observer_.get()); |
| 82 } | 82 } |
| 83 } | 83 } |
| 84 | 84 |
| 85 for (auto sample : samples_) { | 85 for (auto sample : samples_) { |
| 86 delete sample; | 86 delete sample; |
| 87 } | 87 } |
| 88 std::set<Sample*> empty; | 88 std::set<Sample*> empty; |
| 89 samples_.swap(empty); | 89 samples_.swap(empty); |
| (...skipping 22 matching lines...) Expand all Loading... |
| 112 } | 112 } |
| 113 | 113 |
| 114 void SamplingHeapProfiler::OnWeakCallback( | 114 void SamplingHeapProfiler::OnWeakCallback( |
| 115 const WeakCallbackInfo<Sample>& data) { | 115 const WeakCallbackInfo<Sample>& data) { |
| 116 Sample* sample = data.GetParameter(); | 116 Sample* sample = data.GetParameter(); |
| 117 AllocationNode* node = sample->owner; | 117 AllocationNode* node = sample->owner; |
| 118 DCHECK(node->allocations_[sample->size] > 0); | 118 DCHECK(node->allocations_[sample->size] > 0); |
| 119 node->allocations_[sample->size]--; | 119 node->allocations_[sample->size]--; |
| 120 if (node->allocations_[sample->size] == 0) { | 120 if (node->allocations_[sample->size] == 0) { |
| 121 node->allocations_.erase(sample->size); | 121 node->allocations_.erase(sample->size); |
| 122 while (node->allocations_.empty() && node->children_.empty() && |
| 123 node->parent_ && !node->parent_->pinned_) { |
| 124 AllocationNode* parent = node->parent_; |
| 125 parent->children_.erase( |
| 126 std::find(parent->children_.begin(), parent->children_.end(), node)); |
| 127 delete node; |
| 128 node = parent; |
| 129 } |
| 122 } | 130 } |
| 123 sample->profiler->samples_.erase(sample); | 131 sample->profiler->samples_.erase(sample); |
| 124 delete sample; | 132 delete sample; |
| 125 } | 133 } |
| 126 | 134 |
| 127 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::FindOrAddChildNode( | 135 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::FindOrAddChildNode( |
| 128 AllocationNode* parent, const char* name, int script_id, | 136 AllocationNode* parent, const char* name, int script_id, |
| 129 int start_position) { | 137 int start_position) { |
| 130 for (AllocationNode* child : parent->children_) { | 138 for (AllocationNode* child : parent->children_) { |
| 131 if (child->script_id_ == script_id && | 139 if (child->script_id_ == script_id && |
| 132 child->script_position_ == start_position && | 140 child->script_position_ == start_position && |
| 133 strcmp(child->name_, name) == 0) { | 141 strcmp(child->name_, name) == 0) { |
| 134 return child; | 142 return child; |
| 135 } | 143 } |
| 136 } | 144 } |
| 137 AllocationNode* child = new AllocationNode(name, script_id, start_position); | 145 AllocationNode* child = |
| 146 new AllocationNode(parent, name, script_id, start_position); |
| 138 parent->children_.push_back(child); | 147 parent->children_.push_back(child); |
| 139 return child; | 148 return child; |
| 140 } | 149 } |
| 141 | 150 |
| 142 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::AddStack() { | 151 SamplingHeapProfiler::AllocationNode* SamplingHeapProfiler::AddStack() { |
| 143 AllocationNode* node = &profile_root_; | 152 AllocationNode* node = &profile_root_; |
| 144 | 153 |
| 145 std::vector<SharedFunctionInfo*> stack; | 154 std::vector<SharedFunctionInfo*> stack; |
| 146 StackTraceFrameIterator it(isolate_); | 155 StackTraceFrameIterator it(isolate_); |
| 147 int frames_captured = 0; | 156 int frames_captured = 0; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 script_id = script->id(); | 199 script_id = script->id(); |
| 191 } | 200 } |
| 192 node = FindOrAddChildNode(node, name, script_id, shared->start_position()); | 201 node = FindOrAddChildNode(node, name, script_id, shared->start_position()); |
| 193 } | 202 } |
| 194 return node; | 203 return node; |
| 195 } | 204 } |
| 196 | 205 |
| 197 v8::AllocationProfile::Node* SamplingHeapProfiler::TranslateAllocationNode( | 206 v8::AllocationProfile::Node* SamplingHeapProfiler::TranslateAllocationNode( |
| 198 AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node, | 207 AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node, |
| 199 const std::map<int, Handle<Script>>& scripts) { | 208 const std::map<int, Handle<Script>>& scripts) { |
| 209 // By pinning the node we make sure its children won't get disposed if |
| 210 // a GC kicks in during the tree retrieval. |
| 211 node->pinned_ = true; |
| 200 Local<v8::String> script_name = | 212 Local<v8::String> script_name = |
| 201 ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String("")); | 213 ToApiHandle<v8::String>(isolate_->factory()->InternalizeUtf8String("")); |
| 202 int line = v8::AllocationProfile::kNoLineNumberInfo; | 214 int line = v8::AllocationProfile::kNoLineNumberInfo; |
| 203 int column = v8::AllocationProfile::kNoColumnNumberInfo; | 215 int column = v8::AllocationProfile::kNoColumnNumberInfo; |
| 204 std::vector<v8::AllocationProfile::Allocation> allocations; | 216 std::vector<v8::AllocationProfile::Allocation> allocations; |
| 205 allocations.reserve(node->allocations_.size()); | 217 allocations.reserve(node->allocations_.size()); |
| 206 if (node->script_id_ != v8::UnboundScript::kNoScriptId && | 218 if (node->script_id_ != v8::UnboundScript::kNoScriptId && |
| 207 scripts.find(node->script_id_) != scripts.end()) { | 219 scripts.find(node->script_id_) != scripts.end()) { |
| 208 // Cannot use std::map<T>::at because it is not available on android. | 220 // Cannot use std::map<T>::at because it is not available on android. |
| 209 auto non_const_scripts = | 221 auto non_const_scripts = |
| (...skipping 22 matching lines...) Expand all Loading... |
| 232 size_t child_len = node->children_.size(); | 244 size_t child_len = node->children_.size(); |
| 233 // The children vector may have nodes appended to it during translation | 245 // The children vector may have nodes appended to it during translation |
| 234 // because the translation may allocate strings on the JS heap that have | 246 // because the translation may allocate strings on the JS heap that have |
| 235 // the potential to be sampled. We cache the length of the vector before | 247 // the potential to be sampled. We cache the length of the vector before |
| 236 // iteration so that nodes appended to the vector during iteration are | 248 // iteration so that nodes appended to the vector during iteration are |
| 237 // not processed. | 249 // not processed. |
| 238 for (size_t i = 0; i < child_len; i++) { | 250 for (size_t i = 0; i < child_len; i++) { |
| 239 current->children.push_back( | 251 current->children.push_back( |
| 240 TranslateAllocationNode(profile, node->children_[i], scripts)); | 252 TranslateAllocationNode(profile, node->children_[i], scripts)); |
| 241 } | 253 } |
| 254 node->pinned_ = false; |
| 242 return current; | 255 return current; |
| 243 } | 256 } |
| 244 | 257 |
| 245 v8::AllocationProfile* SamplingHeapProfiler::GetAllocationProfile() { | 258 v8::AllocationProfile* SamplingHeapProfiler::GetAllocationProfile() { |
| 246 // To resolve positions to line/column numbers, we will need to look up | 259 // To resolve positions to line/column numbers, we will need to look up |
| 247 // scripts. Build a map to allow fast mapping from script id to script. | 260 // scripts. Build a map to allow fast mapping from script id to script. |
| 248 std::map<int, Handle<Script>> scripts; | 261 std::map<int, Handle<Script>> scripts; |
| 249 { | 262 { |
| 250 Script::Iterator iterator(isolate_); | 263 Script::Iterator iterator(isolate_); |
| 251 while (Script* script = iterator.Next()) { | 264 while (Script* script = iterator.Next()) { |
| 252 scripts[script->id()] = handle(script); | 265 scripts[script->id()] = handle(script); |
| 253 } | 266 } |
| 254 } | 267 } |
| 255 auto profile = new v8::internal::AllocationProfile(); | 268 auto profile = new v8::internal::AllocationProfile(); |
| 256 TranslateAllocationNode(profile, &profile_root_, scripts); | 269 TranslateAllocationNode(profile, &profile_root_, scripts); |
| 257 return profile; | 270 return profile; |
| 258 } | 271 } |
| 259 | 272 |
| 260 | 273 |
| 261 } // namespace internal | 274 } // namespace internal |
| 262 } // namespace v8 | 275 } // namespace v8 |
| OLD | NEW |