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