| // Copyright 2015 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_PROFILER_SAMPLING_HEAP_PROFILER_H_ |
| #define V8_PROFILER_SAMPLING_HEAP_PROFILER_H_ |
| |
| #include <deque> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include "include/v8-profiler.h" |
| #include "src/heap/heap.h" |
| #include "src/profiler/strings-storage.h" |
| |
| namespace v8 { |
| |
| namespace base { |
| class RandomNumberGenerator; |
| } |
| |
| namespace internal { |
| |
| class SamplingAllocationObserver; |
| |
| class AllocationProfile : public v8::AllocationProfile { |
| public: |
| AllocationProfile() : nodes_() {} |
| |
| v8::AllocationProfile::Node* GetRootNode() override { |
| return nodes_.size() == 0 ? nullptr : &nodes_.front(); |
| } |
| |
| std::deque<v8::AllocationProfile::Node>& nodes() { return nodes_; } |
| |
| private: |
| std::deque<v8::AllocationProfile::Node> nodes_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AllocationProfile); |
| }; |
| |
| class SamplingHeapProfiler { |
| public: |
| SamplingHeapProfiler(Heap* heap, StringsStorage* names, uint64_t rate, |
| int stack_depth, v8::HeapProfiler::SamplingFlags flags); |
| ~SamplingHeapProfiler(); |
| |
| v8::AllocationProfile* GetAllocationProfile(); |
| |
| StringsStorage* names() const { return names_; } |
| |
| class AllocationNode; |
| |
| struct Sample { |
| public: |
| Sample(size_t size_, AllocationNode* owner_, Local<Value> local_, |
| SamplingHeapProfiler* profiler_) |
| : size(size_), |
| owner(owner_), |
| global(Global<Value>( |
| reinterpret_cast<v8::Isolate*>(profiler_->isolate_), local_)), |
| profiler(profiler_) {} |
| ~Sample() { global.Reset(); } |
| const size_t size; |
| AllocationNode* const owner; |
| Global<Value> global; |
| SamplingHeapProfiler* const profiler; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(Sample); |
| }; |
| |
| class AllocationNode { |
| public: |
| AllocationNode(AllocationNode* parent, const char* name, int script_id, |
| int start_position) |
| : parent_(parent), |
| script_id_(script_id), |
| script_position_(start_position), |
| name_(name), |
| pinned_(false) {} |
| ~AllocationNode() { |
| for (auto child : children_) { |
| delete child.second; |
| } |
| } |
| |
| private: |
| typedef uint64_t FunctionId; |
| static FunctionId function_id(int script_id, int start_position, |
| const char* name) { |
| // script_id == kNoScriptId case: |
| // Use function name pointer as an id. Names derived from VM state |
| // must not collide with the builtin names. The least significant bit |
| // of the id is set to 1. |
| if (script_id == v8::UnboundScript::kNoScriptId) { |
| return reinterpret_cast<intptr_t>(name) | 1; |
| } |
| // script_id != kNoScriptId case: |
| // Use script_id, start_position pair to uniquelly identify the node. |
| // The least significant bit of the id is set to 0. |
| DCHECK(static_cast<unsigned>(start_position) < (1u << 31)); |
| return (static_cast<uint64_t>(script_id) << 32) + (start_position << 1); |
| } |
| AllocationNode* FindOrAddChildNode(const char* name, int script_id, |
| int start_position); |
| // TODO(alph): make use of unordered_map's here. Pay attention to |
| // iterator invalidation during TranslateAllocationNode. |
| std::map<size_t, unsigned int> allocations_; |
| std::map<FunctionId, AllocationNode*> children_; |
| AllocationNode* const parent_; |
| const int script_id_; |
| const int script_position_; |
| const char* const name_; |
| bool pinned_; |
| |
| friend class SamplingHeapProfiler; |
| |
| DISALLOW_COPY_AND_ASSIGN(AllocationNode); |
| }; |
| |
| private: |
| Heap* heap() const { return heap_; } |
| |
| void SampleObject(Address soon_object, size_t size); |
| |
| static void OnWeakCallback(const WeakCallbackInfo<Sample>& data); |
| |
| // Methods that construct v8::AllocationProfile. |
| |
| // Translates the provided AllocationNode *node* returning an equivalent |
| // AllocationProfile::Node. The newly created AllocationProfile::Node is added |
| // to the provided AllocationProfile *profile*. Line numbers, column numbers, |
| // and script names are resolved using *scripts* which maps all currently |
| // loaded scripts keyed by their script id. |
| v8::AllocationProfile::Node* TranslateAllocationNode( |
| AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node, |
| const std::map<int, Handle<Script>>& scripts); |
| v8::AllocationProfile::Allocation ScaleSample(size_t size, |
| unsigned int count); |
| AllocationNode* AddStack(); |
| |
| Isolate* const isolate_; |
| Heap* const heap_; |
| std::unique_ptr<SamplingAllocationObserver> new_space_observer_; |
| std::unique_ptr<SamplingAllocationObserver> other_spaces_observer_; |
| StringsStorage* const names_; |
| AllocationNode profile_root_; |
| std::set<Sample*> samples_; |
| const int stack_depth_; |
| const uint64_t rate_; |
| v8::HeapProfiler::SamplingFlags flags_; |
| |
| friend class SamplingAllocationObserver; |
| |
| DISALLOW_COPY_AND_ASSIGN(SamplingHeapProfiler); |
| }; |
| |
| class SamplingAllocationObserver : public AllocationObserver { |
| public: |
| SamplingAllocationObserver(Heap* heap, intptr_t step_size, uint64_t rate, |
| SamplingHeapProfiler* profiler, |
| base::RandomNumberGenerator* random) |
| : AllocationObserver(step_size), |
| profiler_(profiler), |
| heap_(heap), |
| random_(random), |
| rate_(rate) {} |
| virtual ~SamplingAllocationObserver() {} |
| |
| protected: |
| void Step(int bytes_allocated, Address soon_object, size_t size) override { |
| USE(heap_); |
| DCHECK(heap_->gc_state() == Heap::NOT_IN_GC); |
| if (soon_object) { |
| // TODO(ofrobots): it would be better to sample the next object rather |
| // than skipping this sample epoch if soon_object happens to be null. |
| profiler_->SampleObject(soon_object, size); |
| } |
| } |
| |
| intptr_t GetNextStepSize() override { return GetNextSampleInterval(rate_); } |
| |
| private: |
| intptr_t GetNextSampleInterval(uint64_t rate); |
| SamplingHeapProfiler* const profiler_; |
| Heap* const heap_; |
| base::RandomNumberGenerator* const random_; |
| uint64_t const rate_; |
| }; |
| |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_PROFILER_SAMPLING_HEAP_PROFILER_H_ |