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