// 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 #include #include #include #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& nodes() { return nodes_; } private: std::deque 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 local_, SamplingHeapProfiler* profiler_) : size(size_), owner(owner_), global(Global( reinterpret_cast(profiler_->isolate_), local_)), profiler(profiler_) {} ~Sample() { global.Reset(); } const size_t size; AllocationNode* const owner; Global 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(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(start_position) < (1u << 31)); return (static_cast(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 allocations_; std::map 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& 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>& scripts); v8::AllocationProfile::Allocation ScaleSample(size_t size, unsigned int count); AllocationNode* AddStack(); Isolate* const isolate_; Heap* const heap_; std::unique_ptr new_space_observer_; std::unique_ptr other_spaces_observer_; StringsStorage* const names_; AllocationNode profile_root_; std::set> 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_