• 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 <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