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 603747 : class 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 3075006 : 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 603871 : void set_script_id(int script_id) { script_id_ = script_id; }
78 : int position() const { return position_; }
79 603871 : 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 8391 : 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 67097 : return rare_data_ && rare_data_->deopt_id_ != kNoDeoptimizationId;
93 : }
94 : void clear_deopt_info() {
95 6 : if (!rare_data_) return;
96 : // TODO(alph): Clear rare_data_ if that was the only field in use.
97 6 : rare_data_->deopt_reason_ = kNoDeoptReason;
98 6 : rare_data_->deopt_id_ = kNoDeoptimizationId;
99 : }
100 415992 : 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 87 : return lhs.get()->IsSameFunctionAs(rhs.get());
123 : }
124 : };
125 : struct Hasher {
126 : std::size_t operator()(const std::unique_ptr<CodeEntry>& e) const {
127 171 : 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 : static const char* const kEmptyResourceName;
148 : static const char* const kEmptyBailoutReason;
149 : static const char* const kNoDeoptReason;
150 :
151 : static const char* const kProgramEntryName;
152 : 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 : static const char* const kUnresolvedFunctionName;
157 :
158 : V8_INLINE static CodeEntry* program_entry() {
159 1447 : return kProgramEntry.Pointer();
160 : }
161 15 : V8_INLINE static CodeEntry* idle_entry() { return kIdleEntry.Pointer(); }
162 1790 : V8_INLINE static CodeEntry* gc_entry() { return kGCEntry.Pointer(); }
163 : V8_INLINE static CodeEntry* unresolved_entry() {
164 : return kUnresolvedEntry.Pointer();
165 : }
166 :
167 : void print() const;
168 :
169 : private:
170 462 : struct RareData {
171 : const char* deopt_reason_ = kNoDeoptReason;
172 : const char* bailout_reason_ = kEmptyBailoutReason;
173 : int deopt_id_ = kNoDeoptimizationId;
174 : std::unordered_map<int, std::vector<CodeEntryAndLineNumber>> inline_stacks_;
175 : std::unordered_set<std::unique_ptr<CodeEntry>, Hasher, Equals>
176 : inline_entries_;
177 : std::vector<CpuProfileDeoptFrame> deopt_inlined_frames_;
178 : };
179 :
180 : RareData* EnsureRareData();
181 :
182 : struct ProgramEntryCreateTrait {
183 : static CodeEntry* Create();
184 : };
185 : struct IdleEntryCreateTrait {
186 : static CodeEntry* Create();
187 : };
188 : struct GCEntryCreateTrait {
189 : static CodeEntry* Create();
190 : };
191 : struct UnresolvedEntryCreateTrait {
192 : static CodeEntry* Create();
193 : };
194 :
195 : static base::LazyDynamicInstance<CodeEntry, ProgramEntryCreateTrait>::type
196 : kProgramEntry;
197 : static base::LazyDynamicInstance<CodeEntry, IdleEntryCreateTrait>::type
198 : kIdleEntry;
199 : static base::LazyDynamicInstance<CodeEntry, GCEntryCreateTrait>::type
200 : kGCEntry;
201 : static base::LazyDynamicInstance<CodeEntry, UnresolvedEntryCreateTrait>::type
202 : kUnresolvedEntry;
203 :
204 : using TagField = BitField<CodeEventListener::LogEventsAndTags, 0, 8>;
205 : using BuiltinIdField = BitField<Builtins::Name, 8, 22>;
206 : static_assert(Builtins::builtin_count <= BuiltinIdField::kNumValues,
207 : "builtin_count exceeds size of bitfield");
208 : using UsedField = BitField<bool, 30, 1>;
209 : using SharedCrossOriginField = BitField<bool, 31, 1>;
210 :
211 : uint32_t bit_field_;
212 : const char* name_;
213 : const char* resource_name_;
214 : int line_number_;
215 : int column_number_;
216 : int script_id_;
217 : int position_;
218 : std::unique_ptr<SourcePositionTable> line_info_;
219 : Address instruction_start_;
220 : std::unique_ptr<RareData> rare_data_;
221 :
222 : DISALLOW_COPY_AND_ASSIGN(CodeEntry);
223 : };
224 :
225 : struct CodeEntryAndLineNumber {
226 : CodeEntry* code_entry;
227 : int line_number;
228 : };
229 :
230 : typedef std::vector<CodeEntryAndLineNumber> ProfileStackTrace;
231 :
232 : class ProfileTree;
233 :
234 11640 : class ProfileNode {
235 : public:
236 : inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent,
237 : int line_number = 0);
238 :
239 : ProfileNode* FindChild(
240 : CodeEntry* entry,
241 : int line_number = v8::CpuProfileNode::kNoLineNumberInfo);
242 : ProfileNode* FindOrAddChild(CodeEntry* entry, int line_number = 0);
243 66141 : void IncrementSelfTicks() { ++self_ticks_; }
244 : void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; }
245 : void IncrementLineTicks(int src_line);
246 :
247 : CodeEntry* entry() const { return entry_; }
248 : unsigned self_ticks() const { return self_ticks_; }
249 : const std::vector<ProfileNode*>* children() const { return &children_list_; }
250 : unsigned id() const { return id_; }
251 : unsigned function_id() const;
252 : ProfileNode* parent() const { return parent_; }
253 : int line_number() const {
254 567 : return line_number_ != 0 ? line_number_ : entry_->line_number();
255 : }
256 :
257 : unsigned int GetHitLineCount() const {
258 356 : return static_cast<unsigned int>(line_ticks_.size());
259 : }
260 : bool GetLineTicks(v8::CpuProfileNode::LineTick* entries,
261 : unsigned int length) const;
262 : void CollectDeoptInfo(CodeEntry* entry);
263 : const std::vector<CpuProfileDeoptInfo>& deopt_infos() const {
264 0 : return deopt_infos_;
265 : }
266 : Isolate* isolate() const;
267 :
268 : void Print(int indent);
269 :
270 : private:
271 : struct Equals {
272 : bool operator()(CodeEntryAndLineNumber lhs,
273 : CodeEntryAndLineNumber rhs) const {
274 218072 : return lhs.code_entry->IsSameFunctionAs(rhs.code_entry) &&
275 : lhs.line_number == rhs.line_number;
276 : }
277 : };
278 : struct Hasher {
279 226822 : std::size_t operator()(CodeEntryAndLineNumber pair) const {
280 453644 : return pair.code_entry->GetHash() ^ ComputeUnseededHash(pair.line_number);
281 : }
282 : };
283 :
284 : ProfileTree* tree_;
285 : CodeEntry* entry_;
286 : unsigned self_ticks_;
287 : std::unordered_map<CodeEntryAndLineNumber, ProfileNode*, Hasher, Equals>
288 : children_;
289 : int line_number_;
290 : std::vector<ProfileNode*> children_list_;
291 : ProfileNode* parent_;
292 : unsigned id_;
293 : // maps line number --> number of ticks
294 : std::unordered_map<int, int> line_ticks_;
295 :
296 : std::vector<CpuProfileDeoptInfo> deopt_infos_;
297 :
298 : DISALLOW_COPY_AND_ASSIGN(ProfileNode);
299 : };
300 :
301 : class ProfileTree {
302 : public:
303 : explicit ProfileTree(Isolate* isolate);
304 : ~ProfileTree();
305 :
306 : typedef v8::CpuProfilingMode ProfilingMode;
307 :
308 : ProfileNode* AddPathFromEnd(
309 : const std::vector<CodeEntry*>& path,
310 : int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
311 : bool update_stats = true);
312 : ProfileNode* AddPathFromEnd(
313 : const ProfileStackTrace& path,
314 : int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
315 : bool update_stats = true,
316 : ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
317 : ProfileNode* root() const { return root_; }
318 5820 : unsigned next_node_id() { return next_node_id_++; }
319 : unsigned GetFunctionId(const ProfileNode* node);
320 :
321 : void Print() {
322 594 : root_->Print(0);
323 : }
324 :
325 : Isolate* isolate() const { return isolate_; }
326 :
327 5820 : void EnqueueNode(const ProfileNode* node) { pending_nodes_.push_back(node); }
328 : size_t pending_nodes_count() const { return pending_nodes_.size(); }
329 : std::vector<const ProfileNode*> TakePendingNodes() {
330 : return std::move(pending_nodes_);
331 : }
332 :
333 : private:
334 : template <typename Callback>
335 : void TraverseDepthFirst(Callback* callback);
336 :
337 : std::vector<const ProfileNode*> pending_nodes_;
338 :
339 : CodeEntry root_entry_;
340 : unsigned next_node_id_;
341 : ProfileNode* root_;
342 : Isolate* isolate_;
343 :
344 : unsigned next_function_id_;
345 : std::unordered_map<CodeEntry*, unsigned> function_ids_;
346 :
347 : DISALLOW_COPY_AND_ASSIGN(ProfileTree);
348 : };
349 :
350 : class CpuProfiler;
351 :
352 2880 : class CpuProfile {
353 : public:
354 : typedef v8::CpuProfilingMode ProfilingMode;
355 :
356 : struct SampleInfo {
357 : ProfileNode* node;
358 : base::TimeTicks timestamp;
359 : int line;
360 : };
361 :
362 : CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples,
363 : ProfilingMode mode);
364 :
365 : // Add pc -> ... -> main() call path to the profile.
366 : void AddPath(base::TimeTicks timestamp, const ProfileStackTrace& path,
367 : int src_line, bool update_stats);
368 : void FinishProfile();
369 :
370 : const char* title() const { return title_; }
371 5 : const ProfileTree* top_down() const { return &top_down_; }
372 :
373 6548 : int samples_count() const { return static_cast<int>(samples_.size()); }
374 : const SampleInfo& sample(int index) const { return samples_[index]; }
375 :
376 : base::TimeTicks start_time() const { return start_time_; }
377 : base::TimeTicks end_time() const { return end_time_; }
378 : CpuProfiler* cpu_profiler() const { return profiler_; }
379 :
380 : void UpdateTicksScale();
381 :
382 : void Print();
383 :
384 : private:
385 : void StreamPendingTraceEvents();
386 :
387 : const char* title_;
388 : bool record_samples_;
389 : ProfilingMode mode_;
390 : base::TimeTicks start_time_;
391 : base::TimeTicks end_time_;
392 : std::deque<SampleInfo> samples_;
393 : ProfileTree top_down_;
394 : CpuProfiler* const profiler_;
395 : size_t streaming_next_sample_;
396 : uint32_t id_;
397 :
398 : static std::atomic<uint32_t> last_id_;
399 :
400 : DISALLOW_COPY_AND_ASSIGN(CpuProfile);
401 : };
402 :
403 1630 : class CodeMap {
404 : public:
405 : CodeMap();
406 : ~CodeMap();
407 :
408 : void AddCode(Address addr, CodeEntry* entry, unsigned size);
409 : void MoveCode(Address from, Address to);
410 : CodeEntry* FindEntry(Address addr);
411 : void Print();
412 :
413 : private:
414 : struct CodeEntryMapInfo {
415 : unsigned index;
416 : unsigned size;
417 : };
418 :
419 : union CodeEntrySlotInfo {
420 : CodeEntry* entry;
421 : unsigned next_free_slot;
422 : };
423 :
424 : static constexpr unsigned kNoFreeSlot = std::numeric_limits<unsigned>::max();
425 :
426 : void ClearCodesInRange(Address start, Address end);
427 : unsigned AddCodeEntry(Address start, CodeEntry*);
428 : void DeleteCodeEntry(unsigned index);
429 :
430 241932 : CodeEntry* entry(unsigned index) { return code_entries_[index].entry; }
431 :
432 : std::deque<CodeEntrySlotInfo> code_entries_;
433 : std::map<Address, CodeEntryMapInfo> code_map_;
434 : unsigned free_list_head_ = kNoFreeSlot;
435 :
436 : DISALLOW_COPY_AND_ASSIGN(CodeMap);
437 : };
438 :
439 998 : class CpuProfilesCollection {
440 : public:
441 : explicit CpuProfilesCollection(Isolate* isolate);
442 :
443 : typedef v8::CpuProfilingMode ProfilingMode;
444 :
445 993 : void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; }
446 : bool StartProfiling(const char* title, bool record_samples,
447 : ProfilingMode mode = ProfilingMode::kLeafNodeLineNumbers);
448 : CpuProfile* StopProfiling(const char* title);
449 : std::vector<std::unique_ptr<CpuProfile>>* profiles() {
450 : return &finished_profiles_;
451 : }
452 1647 : const char* GetName(Name name) { return resource_names_.GetName(name); }
453 : bool IsLastProfile(const char* title);
454 : void RemoveProfile(CpuProfile* profile);
455 :
456 : // Called from profile generator thread.
457 : void AddPathToCurrentProfiles(base::TimeTicks timestamp,
458 : const ProfileStackTrace& path, int src_line,
459 : bool update_stats);
460 :
461 : // Limits the number of profiles that can be simultaneously collected.
462 : static const int kMaxSimultaneousProfiles = 100;
463 :
464 : private:
465 : StringsStorage resource_names_;
466 : std::vector<std::unique_ptr<CpuProfile>> finished_profiles_;
467 : CpuProfiler* profiler_;
468 :
469 : // Accessed by VM thread and profile generator thread.
470 : std::vector<std::unique_ptr<CpuProfile>> current_profiles_;
471 : base::Semaphore current_profiles_semaphore_;
472 :
473 : DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection);
474 : };
475 :
476 800 : class ProfileGenerator {
477 : public:
478 : explicit ProfileGenerator(CpuProfilesCollection* profiles);
479 :
480 : void RecordTickSample(const TickSample& sample);
481 :
482 1817252 : CodeMap* code_map() { return &code_map_; }
483 :
484 : private:
485 : CodeEntry* FindEntry(Address address);
486 : CodeEntry* EntryForVMState(StateTag tag);
487 :
488 : CpuProfilesCollection* profiles_;
489 : CodeMap code_map_;
490 :
491 : DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
492 : };
493 :
494 : } // namespace internal
495 : } // namespace v8
496 :
497 : #endif // V8_PROFILER_PROFILE_GENERATOR_H_
|