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