Line data Source code
1 : // Copyright 2011 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_PROFILE_GENERATOR_H_
6 : #define V8_PROFILER_PROFILE_GENERATOR_H_
7 :
8 : #include <atomic>
9 : #include <deque>
10 : #include <limits>
11 : #include <map>
12 : #include <memory>
13 : #include <unordered_map>
14 : #include <utility>
15 : #include <vector>
16 :
17 : #include "include/v8-profiler.h"
18 : #include "src/allocation.h"
19 : #include "src/builtins/builtins.h"
20 : #include "src/code-events.h"
21 : #include "src/profiler/strings-storage.h"
22 : #include "src/source-position.h"
23 :
24 : namespace v8 {
25 : namespace internal {
26 :
27 : struct TickSample;
28 :
29 : // Provides a mapping from the offsets within generated code or a bytecode array
30 : // to the source line and inlining id.
31 : class SourcePositionTable : public Malloced {
32 : public:
33 : SourcePositionTable() = default;
34 :
35 : void SetPosition(int pc_offset, int line, int inlining_id);
36 : int GetSourceLineNumber(int pc_offset) const;
37 : int GetInliningId(int pc_offset) const;
38 :
39 : void print() const;
40 :
41 : private:
42 : struct SourcePositionTuple {
43 : bool operator<(const SourcePositionTuple& other) const {
44 : return pc_offset < other.pc_offset;
45 : }
46 : int pc_offset;
47 : int line_number;
48 : int inlining_id;
49 : };
50 : // This is logically a map, but we store it as a vector of tuples, sorted by
51 : // the pc offset, so that we can save space and look up items using binary
52 : // search.
53 : std::vector<SourcePositionTuple> pc_offsets_to_lines_;
54 : DISALLOW_COPY_AND_ASSIGN(SourcePositionTable);
55 : };
56 :
57 : struct CodeEntryAndLineNumber;
58 :
59 2962403 : class CodeEntry {
60 : public:
61 : // CodeEntry doesn't own name strings, just references them.
62 : inline CodeEntry(CodeEventListener::LogEventsAndTags tag, const char* name,
63 : const char* resource_name = CodeEntry::kEmptyResourceName,
64 : int line_number = v8::CpuProfileNode::kNoLineNumberInfo,
65 : int column_number = v8::CpuProfileNode::kNoColumnNumberInfo,
66 : std::unique_ptr<SourcePositionTable> line_info = nullptr,
67 : Address instruction_start = kNullAddress);
68 :
69 : const char* name() const { return name_; }
70 : const char* resource_name() const { return resource_name_; }
71 : int line_number() const { return line_number_; }
72 : int column_number() const { return column_number_; }
73 : const SourcePositionTable* line_info() const { return line_info_.get(); }
74 : int script_id() const { return script_id_; }
75 603431 : void set_script_id(int script_id) { script_id_ = script_id; }
76 : int position() const { return position_; }
77 603431 : void set_position(int position) { position_ = position; }
78 0 : void set_bailout_reason(const char* bailout_reason) {
79 61 : EnsureRareData()->bailout_reason_ = bailout_reason;
80 0 : }
81 : const char* bailout_reason() const {
82 4604 : return rare_data_ ? rare_data_->bailout_reason_ : kEmptyBailoutReason;
83 : }
84 :
85 : void set_deopt_info(const char* deopt_reason, int deopt_id,
86 : std::vector<CpuProfileDeoptFrame> inlined_frames);
87 :
88 : CpuProfileDeoptInfo GetDeoptInfo();
89 : bool has_deopt_info() const {
90 28037 : return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId;
91 : }
92 : void clear_deopt_info() {
93 0 : if (!rare_data_) return;
94 : // TODO(alph): Clear rare_data_ if that was the only field in use.
95 0 : rare_data_->deopt_reason_ = kNoDeoptReason;
96 0 : rare_data_->deopt_id_ = kNoDeoptimizationId;
97 : }
98 128936 : void mark_used() { bit_field_ = UsedField::update(bit_field_, true); }
99 : bool used() const { return UsedField::decode(bit_field_); }
100 :
101 : void FillFunctionInfo(SharedFunctionInfo shared);
102 :
103 : void SetBuiltinId(Builtins::Name id);
104 : Builtins::Name builtin_id() const {
105 : return BuiltinIdField::decode(bit_field_);
106 : }
107 :
108 : uint32_t GetHash() const;
109 : bool IsSameFunctionAs(const CodeEntry* entry) const;
110 :
111 : int GetSourceLine(int pc_offset) const;
112 :
113 : struct Equals {
114 : bool operator()(const std::unique_ptr<CodeEntry>& lhs,
115 : const std::unique_ptr<CodeEntry>& rhs) const {
116 104 : return lhs.get()->IsSameFunctionAs(rhs.get());
117 : }
118 : };
119 : struct Hasher {
120 : std::size_t operator()(const std::unique_ptr<CodeEntry>& e) const {
121 164 : return e->GetHash();
122 : }
123 : };
124 :
125 : void SetInlineStacks(
126 : std::unordered_set<std::unique_ptr<CodeEntry>, Hasher, Equals>
127 : inline_entries,
128 : std::unordered_map<int, std::vector<CodeEntryAndLineNumber>>
129 : inline_stacks);
130 : const std::vector<CodeEntryAndLineNumber>* GetInlineStack(
131 : int pc_offset) const;
132 :
133 10 : void set_instruction_start(Address start) { instruction_start_ = start; }
134 : Address instruction_start() const { return instruction_start_; }
135 :
136 : CodeEventListener::LogEventsAndTags tag() const {
137 : return TagField::decode(bit_field_);
138 : }
139 :
140 : static const char* const kWasmResourceNamePrefix;
141 : static const char* const kEmptyResourceName;
142 : static const char* const kEmptyBailoutReason;
143 : static const char* const kNoDeoptReason;
144 :
145 : static const char* const kProgramEntryName;
146 : static const char* const kIdleEntryName;
147 : static const char* const kGarbageCollectorEntryName;
148 : // Used to represent frames for which we have no reliable way to
149 : // detect function.
150 : static const char* const kUnresolvedFunctionName;
151 :
152 : V8_INLINE static CodeEntry* program_entry() {
153 1437 : return kProgramEntry.Pointer();
154 : }
155 15 : V8_INLINE static CodeEntry* idle_entry() { return kIdleEntry.Pointer(); }
156 682 : V8_INLINE static CodeEntry* gc_entry() { return kGCEntry.Pointer(); }
157 : V8_INLINE static CodeEntry* unresolved_entry() {
158 0 : return kUnresolvedEntry.Pointer();
159 : }
160 :
161 : void print() const;
162 :
163 : private:
164 426 : struct RareData {
165 : const char* deopt_reason_ = kNoDeoptReason;
166 : const char* bailout_reason_ = kEmptyBailoutReason;
167 : int deopt_id_ = kNoDeoptimizationId;
168 : std::unordered_map<int, std::vector<CodeEntryAndLineNumber>> inline_stacks_;
169 : std::unordered_set<std::unique_ptr<CodeEntry>, Hasher, Equals>
170 : inline_entries_;
171 : std::vector<CpuProfileDeoptFrame> deopt_inlined_frames_;
172 : };
173 :
174 : RareData* EnsureRareData();
175 :
176 : struct ProgramEntryCreateTrait {
177 : static CodeEntry* Create();
178 : };
179 : struct IdleEntryCreateTrait {
180 : static CodeEntry* Create();
181 : };
182 : struct GCEntryCreateTrait {
183 : static CodeEntry* Create();
184 : };
185 : struct UnresolvedEntryCreateTrait {
186 : static CodeEntry* Create();
187 : };
188 :
189 : static base::LazyDynamicInstance<CodeEntry, ProgramEntryCreateTrait>::type
190 : kProgramEntry;
191 : static base::LazyDynamicInstance<CodeEntry, IdleEntryCreateTrait>::type
192 : kIdleEntry;
193 : static base::LazyDynamicInstance<CodeEntry, GCEntryCreateTrait>::type
194 : kGCEntry;
195 : static base::LazyDynamicInstance<CodeEntry, UnresolvedEntryCreateTrait>::type
196 : kUnresolvedEntry;
197 :
198 : using TagField = BitField<CodeEventListener::LogEventsAndTags, 0, 8>;
199 : using BuiltinIdField = BitField<Builtins::Name, 8, 23>;
200 : using UsedField = BitField<bool, 31, 1>;
201 :
202 : uint32_t bit_field_;
203 : const char* name_;
204 : const char* resource_name_;
205 : int line_number_;
206 : int column_number_;
207 : int script_id_;
208 : int position_;
209 : std::unique_ptr<SourcePositionTable> line_info_;
210 : Address instruction_start_;
211 : std::unique_ptr<RareData> rare_data_;
212 :
213 : DISALLOW_COPY_AND_ASSIGN(CodeEntry);
214 : };
215 :
216 : struct CodeEntryAndLineNumber {
217 : CodeEntry* code_entry;
218 : int line_number;
219 : };
220 :
221 : typedef std::vector<CodeEntryAndLineNumber> ProfileStackTrace;
222 :
223 : class ProfileTree;
224 :
225 7712 : class ProfileNode {
226 : public:
227 : inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent,
228 : int line_number = 0);
229 :
230 : ProfileNode* FindChild(
231 : CodeEntry* entry,
232 : int line_number = v8::CpuProfileNode::kNoLineNumberInfo);
233 : ProfileNode* FindOrAddChild(CodeEntry* entry, int line_number = 0);
234 27330 : void IncrementSelfTicks() { ++self_ticks_; }
235 : void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; }
236 : void IncrementLineTicks(int src_line);
237 :
238 : CodeEntry* entry() const { return entry_; }
239 : unsigned self_ticks() const { return self_ticks_; }
240 11398 : const std::vector<ProfileNode*>* children() const { return &children_list_; }
241 : unsigned id() const { return id_; }
242 : unsigned function_id() const;
243 : ProfileNode* parent() const { return parent_; }
244 : int line_number() const {
245 263 : return line_number_ != 0 ? line_number_ : entry_->line_number();
246 : }
247 :
248 : unsigned int GetHitLineCount() const {
249 113 : return static_cast<unsigned int>(line_ticks_.size());
250 : }
251 : bool GetLineTicks(v8::CpuProfileNode::LineTick* entries,
252 : unsigned int length) const;
253 : void CollectDeoptInfo(CodeEntry* entry);
254 : const std::vector<CpuProfileDeoptInfo>& deopt_infos() const {
255 : return deopt_infos_;
256 : }
257 : Isolate* isolate() const;
258 :
259 : void Print(int indent);
260 :
261 : private:
262 : struct Equals {
263 : bool operator()(CodeEntryAndLineNumber lhs,
264 : CodeEntryAndLineNumber rhs) const {
265 72720 : return lhs.code_entry->IsSameFunctionAs(rhs.code_entry) &&
266 : lhs.line_number == rhs.line_number;
267 : }
268 : };
269 : struct Hasher {
270 77828 : std::size_t operator()(CodeEntryAndLineNumber pair) const {
271 155656 : return pair.code_entry->GetHash() ^ ComputeUnseededHash(pair.line_number);
272 : }
273 : };
274 :
275 : ProfileTree* tree_;
276 : CodeEntry* entry_;
277 : unsigned self_ticks_;
278 : std::unordered_map<CodeEntryAndLineNumber, ProfileNode*, Hasher, Equals>
279 : children_;
280 : int line_number_;
281 : std::vector<ProfileNode*> children_list_;
282 : ProfileNode* parent_;
283 : unsigned id_;
284 : // maps line number --> number of ticks
285 : std::unordered_map<int, int> line_ticks_;
286 :
287 : std::vector<CpuProfileDeoptInfo> deopt_infos_;
288 :
289 : DISALLOW_COPY_AND_ASSIGN(ProfileNode);
290 : };
291 :
292 : class ProfileTree {
293 : public:
294 : explicit ProfileTree(Isolate* isolate);
295 : ~ProfileTree();
296 :
297 : typedef v8::CpuProfilingMode ProfilingMode;
298 :
299 : ProfileNode* AddPathFromEnd(
300 : const std::vector<CodeEntry*>& path,
301 : int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
302 : bool update_stats = true);
303 : ProfileNode* AddPathFromEnd(
304 : const ProfileStackTrace& path,
305 : int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
306 : bool update_stats = true,
307 : ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
308 : ProfileNode* root() const { return root_; }
309 3856 : unsigned next_node_id() { return next_node_id_++; }
310 : unsigned GetFunctionId(const ProfileNode* node);
311 :
312 : void Print() {
313 555 : root_->Print(0);
314 : }
315 :
316 : Isolate* isolate() const { return isolate_; }
317 :
318 3856 : void EnqueueNode(const ProfileNode* node) { pending_nodes_.push_back(node); }
319 28004 : size_t pending_nodes_count() const { return pending_nodes_.size(); }
320 : std::vector<const ProfileNode*> TakePendingNodes() {
321 : return std::move(pending_nodes_);
322 : }
323 :
324 : private:
325 : template <typename Callback>
326 : void TraverseDepthFirst(Callback* callback);
327 :
328 : std::vector<const ProfileNode*> pending_nodes_;
329 :
330 : CodeEntry root_entry_;
331 : unsigned next_node_id_;
332 : ProfileNode* root_;
333 : Isolate* isolate_;
334 :
335 : unsigned next_function_id_;
336 : std::unordered_map<CodeEntry*, unsigned> function_ids_;
337 :
338 : DISALLOW_COPY_AND_ASSIGN(ProfileTree);
339 : };
340 :
341 : class CpuProfiler;
342 :
343 1297 : class CpuProfile {
344 : public:
345 : typedef v8::CpuProfilingMode ProfilingMode;
346 :
347 : struct SampleInfo {
348 : ProfileNode* node;
349 : base::TimeTicks timestamp;
350 : int line;
351 : };
352 :
353 : CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples,
354 : ProfilingMode mode);
355 :
356 : // Add pc -> ... -> main() call path to the profile.
357 : void AddPath(base::TimeTicks timestamp, const ProfileStackTrace& path,
358 : int src_line, bool update_stats);
359 : void FinishProfile();
360 :
361 : const char* title() const { return title_; }
362 : const ProfileTree* top_down() const { return &top_down_; }
363 :
364 110 : int samples_count() const { return static_cast<int>(samples_.size()); }
365 : const SampleInfo& sample(int index) const { return samples_[index]; }
366 :
367 : base::TimeTicks start_time() const { return start_time_; }
368 : base::TimeTicks end_time() const { return end_time_; }
369 : CpuProfiler* cpu_profiler() const { return profiler_; }
370 :
371 : void UpdateTicksScale();
372 :
373 : void Print();
374 :
375 : private:
376 : void StreamPendingTraceEvents();
377 :
378 : const char* title_;
379 : bool record_samples_;
380 : ProfilingMode mode_;
381 : base::TimeTicks start_time_;
382 : base::TimeTicks end_time_;
383 : std::deque<SampleInfo> samples_;
384 : ProfileTree top_down_;
385 : CpuProfiler* const profiler_;
386 : size_t streaming_next_sample_;
387 : uint32_t id_;
388 :
389 : static std::atomic<uint32_t> last_id_;
390 :
391 : DISALLOW_COPY_AND_ASSIGN(CpuProfile);
392 : };
393 :
394 782 : class CodeMap {
395 : public:
396 : CodeMap();
397 : ~CodeMap();
398 :
399 : void AddCode(Address addr, CodeEntry* entry, unsigned size);
400 : void MoveCode(Address from, Address to);
401 : CodeEntry* FindEntry(Address addr);
402 : void Print();
403 :
404 : private:
405 : struct CodeEntryMapInfo {
406 : unsigned index;
407 : unsigned size;
408 : };
409 :
410 : union CodeEntrySlotInfo {
411 : CodeEntry* entry;
412 : unsigned next_free_slot;
413 : };
414 :
415 : static constexpr unsigned kNoFreeSlot = std::numeric_limits<unsigned>::max();
416 :
417 : void ClearCodesInRange(Address start, Address end);
418 : unsigned AddCodeEntry(Address start, CodeEntry*);
419 : void DeleteCodeEntry(unsigned index);
420 :
421 97800 : CodeEntry* entry(unsigned index) { return code_entries_[index].entry; }
422 :
423 : std::deque<CodeEntrySlotInfo> code_entries_;
424 : std::map<Address, CodeEntryMapInfo> code_map_;
425 : unsigned free_list_head_ = kNoFreeSlot;
426 :
427 : DISALLOW_COPY_AND_ASSIGN(CodeMap);
428 : };
429 :
430 922 : class CpuProfilesCollection {
431 : public:
432 : explicit CpuProfilesCollection(Isolate* isolate);
433 :
434 : typedef v8::CpuProfilingMode ProfilingMode;
435 :
436 917 : void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; }
437 : bool StartProfiling(const char* title, bool record_samples,
438 : ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
439 : CpuProfile* StopProfiling(const char* title);
440 : std::vector<std::unique_ptr<CpuProfile>>* profiles() {
441 : return &finished_profiles_;
442 : }
443 1371 : const char* GetName(Name name) { return resource_names_.GetName(name); }
444 : bool IsLastProfile(const char* title);
445 : void RemoveProfile(CpuProfile* profile);
446 :
447 : // Called from profile generator thread.
448 : void AddPathToCurrentProfiles(base::TimeTicks timestamp,
449 : const ProfileStackTrace& path, int src_line,
450 : bool update_stats);
451 :
452 : // Limits the number of profiles that can be simultaneously collected.
453 : static const int kMaxSimultaneousProfiles = 100;
454 :
455 : private:
456 : StringsStorage resource_names_;
457 : std::vector<std::unique_ptr<CpuProfile>> finished_profiles_;
458 : CpuProfiler* profiler_;
459 :
460 : // Accessed by VM thread and profile generator thread.
461 : std::vector<std::unique_ptr<CpuProfile>> current_profiles_;
462 : base::Semaphore current_profiles_semaphore_;
463 :
464 : DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection);
465 : };
466 :
467 757 : class ProfileGenerator {
468 : public:
469 : explicit ProfileGenerator(CpuProfilesCollection* profiles);
470 :
471 : void RecordTickSample(const TickSample& sample);
472 :
473 : CodeMap* code_map() { return &code_map_; }
474 :
475 : private:
476 : CodeEntry* FindEntry(Address address);
477 : CodeEntry* EntryForVMState(StateTag tag);
478 :
479 : CpuProfilesCollection* profiles_;
480 : CodeMap code_map_;
481 :
482 : DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
483 : };
484 :
485 : } // namespace internal
486 : } // namespace v8
487 :
488 : #endif // V8_PROFILER_PROFILE_GENERATOR_H_
|