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
16namespace v8 {
17
18namespace base {
19class RandomNumberGenerator;
20}
21
22namespace internal {
23
24class SamplingAllocationObserver;
25
26class AllocationProfile : public v8::AllocationProfile {
27 public:
28  AllocationProfile() : nodes_() {}
29
30  v8::AllocationProfile::Node* GetRootNode() override {
31    return nodes_.size() == 0 ? nullptr : &nodes_.front();
32  }
33
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
42class 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
50  StringsStorage* names() const { return names_; }
51
52  class AllocationNode;
53
54  struct Sample {
55   public:
56    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_) {}
63    ~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:
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) {}
82    ~AllocationNode() {
83      for (auto child : children_) {
84        delete child.second;
85      }
86    }
87
88   private:
89    typedef uint64_t FunctionId;
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:
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<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
159class SamplingAllocationObserver : public AllocationObserver {
160 public:
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) {}
169  virtual ~SamplingAllocationObserver() {}
170
171 protected:
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    DCHECK(soon_object);
176    profiler_->SampleObject(soon_object, size);
177  }
178
179  intptr_t GetNextStepSize() override { return GetNextSampleInterval(rate_); }
180
181 private:
182  intptr_t GetNextSampleInterval(uint64_t rate);
183  SamplingHeapProfiler* const profiler_;
184  Heap* const heap_;
185  base::RandomNumberGenerator* const random_;
186  uint64_t const rate_;
187};
188
189}  // namespace internal
190}  // namespace v8
191
192#endif  // V8_PROFILER_SAMPLING_HEAP_PROFILER_H_
193