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