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