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 3 // found in the LICENSE file. 4 5 #ifndef V8_PROFILER_SAMPLING_HEAP_PROFILER_H_ 6 #define V8_PROFILER_SAMPLING_HEAP_PROFILER_H_ 7 8 #include <deque> 9 #include <map> 10 #include <memory> 11 #include <set> 12 #include "include/v8-profiler.h" 13 #include "src/heap/heap.h" 14 #include "src/profiler/strings-storage.h" 15 16 namespace v8 { 17 18 namespace base { 19 class RandomNumberGenerator; 20 } 21 22 namespace internal { 23 24 class SamplingAllocationObserver; 25 26 class AllocationProfile : public v8::AllocationProfile { 27 public: AllocationProfile()28 AllocationProfile() : nodes_() {} 29 GetRootNode()30 v8::AllocationProfile::Node* GetRootNode() override { 31 return nodes_.size() == 0 ? nullptr : &nodes_.front(); 32 } 33 nodes()34 std::deque<v8::AllocationProfile::Node>& nodes() { return nodes_; } 35 36 private: 37 std::deque<v8::AllocationProfile::Node> nodes_; 38 39 DISALLOW_COPY_AND_ASSIGN(AllocationProfile); 40 }; 41 42 class SamplingHeapProfiler { 43 public: 44 SamplingHeapProfiler(Heap* heap, StringsStorage* names, uint64_t rate, 45 int stack_depth, v8::HeapProfiler::SamplingFlags flags); 46 ~SamplingHeapProfiler(); 47 48 v8::AllocationProfile* GetAllocationProfile(); 49 names()50 StringsStorage* names() const { return names_; } 51 52 class AllocationNode; 53 54 struct Sample { 55 public: SampleSample56 Sample(size_t size_, AllocationNode* owner_, Local<Value> local_, 57 SamplingHeapProfiler* profiler_) 58 : size(size_), 59 owner(owner_), 60 global(Global<Value>( 61 reinterpret_cast<v8::Isolate*>(profiler_->isolate_), local_)), 62 profiler(profiler_) {} ~SampleSample63 ~Sample() { global.Reset(); } 64 const size_t size; 65 AllocationNode* const owner; 66 Global<Value> global; 67 SamplingHeapProfiler* const profiler; 68 69 private: 70 DISALLOW_COPY_AND_ASSIGN(Sample); 71 }; 72 73 class AllocationNode { 74 public: AllocationNode(AllocationNode * parent,const char * name,int script_id,int start_position)75 AllocationNode(AllocationNode* parent, const char* name, int script_id, 76 int start_position) 77 : parent_(parent), 78 script_id_(script_id), 79 script_position_(start_position), 80 name_(name), 81 pinned_(false) {} ~AllocationNode()82 ~AllocationNode() { 83 for (auto child : children_) { 84 delete child.second; 85 } 86 } 87 88 private: 89 typedef uint64_t FunctionId; function_id(int script_id,int start_position,const char * name)90 static FunctionId function_id(int script_id, int start_position, 91 const char* name) { 92 // script_id == kNoScriptId case: 93 // Use function name pointer as an id. Names derived from VM state 94 // must not collide with the builtin names. The least significant bit 95 // of the id is set to 1. 96 if (script_id == v8::UnboundScript::kNoScriptId) { 97 return reinterpret_cast<intptr_t>(name) | 1; 98 } 99 // script_id != kNoScriptId case: 100 // Use script_id, start_position pair to uniquelly identify the node. 101 // The least significant bit of the id is set to 0. 102 DCHECK(static_cast<unsigned>(start_position) < (1u << 31)); 103 return (static_cast<uint64_t>(script_id) << 32) + (start_position << 1); 104 } 105 AllocationNode* FindOrAddChildNode(const char* name, int script_id, 106 int start_position); 107 // TODO(alph): make use of unordered_map's here. Pay attention to 108 // iterator invalidation during TranslateAllocationNode. 109 std::map<size_t, unsigned int> allocations_; 110 std::map<FunctionId, AllocationNode*> children_; 111 AllocationNode* const parent_; 112 const int script_id_; 113 const int script_position_; 114 const char* const name_; 115 bool pinned_; 116 117 friend class SamplingHeapProfiler; 118 119 DISALLOW_COPY_AND_ASSIGN(AllocationNode); 120 }; 121 122 private: heap()123 Heap* heap() const { return heap_; } 124 125 void SampleObject(Address soon_object, size_t size); 126 127 static void OnWeakCallback(const WeakCallbackInfo<Sample>& data); 128 129 // Methods that construct v8::AllocationProfile. 130 131 // Translates the provided AllocationNode *node* returning an equivalent 132 // AllocationProfile::Node. The newly created AllocationProfile::Node is added 133 // to the provided AllocationProfile *profile*. Line numbers, column numbers, 134 // and script names are resolved using *scripts* which maps all currently 135 // loaded scripts keyed by their script id. 136 v8::AllocationProfile::Node* TranslateAllocationNode( 137 AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node, 138 const std::map<int, Handle<Script>>& scripts); 139 v8::AllocationProfile::Allocation ScaleSample(size_t size, 140 unsigned int count); 141 AllocationNode* AddStack(); 142 143 Isolate* const isolate_; 144 Heap* const heap_; 145 std::unique_ptr<SamplingAllocationObserver> new_space_observer_; 146 std::unique_ptr<SamplingAllocationObserver> other_spaces_observer_; 147 StringsStorage* const names_; 148 AllocationNode profile_root_; 149 std::set<std::unique_ptr<Sample>> samples_; 150 const int stack_depth_; 151 const uint64_t rate_; 152 v8::HeapProfiler::SamplingFlags flags_; 153 154 friend class SamplingAllocationObserver; 155 156 DISALLOW_COPY_AND_ASSIGN(SamplingHeapProfiler); 157 }; 158 159 class SamplingAllocationObserver : public AllocationObserver { 160 public: SamplingAllocationObserver(Heap * heap,intptr_t step_size,uint64_t rate,SamplingHeapProfiler * profiler,base::RandomNumberGenerator * random)161 SamplingAllocationObserver(Heap* heap, intptr_t step_size, uint64_t rate, 162 SamplingHeapProfiler* profiler, 163 base::RandomNumberGenerator* random) 164 : AllocationObserver(step_size), 165 profiler_(profiler), 166 heap_(heap), 167 random_(random), 168 rate_(rate) {} ~SamplingAllocationObserver()169 virtual ~SamplingAllocationObserver() {} 170 171 protected: Step(int bytes_allocated,Address soon_object,size_t size)172 void Step(int bytes_allocated, Address soon_object, size_t size) override { 173 USE(heap_); 174 DCHECK(heap_->gc_state() == Heap::NOT_IN_GC); 175 if (soon_object) { 176 // TODO(ofrobots): it would be better to sample the next object rather 177 // than skipping this sample epoch if soon_object happens to be null. 178 profiler_->SampleObject(soon_object, size); 179 } 180 } 181 GetNextStepSize()182 intptr_t GetNextStepSize() override { return GetNextSampleInterval(rate_); } 183 184 private: 185 intptr_t GetNextSampleInterval(uint64_t rate); 186 SamplingHeapProfiler* const profiler_; 187 Heap* const heap_; 188 base::RandomNumberGenerator* const random_; 189 uint64_t const rate_; 190 }; 191 192 } // namespace internal 193 } // namespace v8 194 195 #endif // V8_PROFILER_SAMPLING_HEAP_PROFILER_H_ 196