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