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
15namespace v8 {
16
17namespace base {
18class RandomNumberGenerator;
19}
20
21namespace internal {
22
23class SamplingAllocationObserver;
24
25class 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
41class 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
156class 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