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 <map>
9 : #include <vector>
10 :
11 : #include "src/allocation.h"
12 : #include "src/base/hashmap.h"
13 : #include "src/log.h"
14 : #include "src/profiler/strings-storage.h"
15 : #include "src/source-position.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 :
20 : struct TickSample;
21 :
22 : // Provides a mapping from the offsets within generated code to
23 : // the source line.
24 : class JITLineInfoTable : public Malloced {
25 : public:
26 : JITLineInfoTable();
27 : ~JITLineInfoTable();
28 :
29 : void SetPosition(int pc_offset, int line);
30 : int GetSourceLineNumber(int pc_offset) const;
31 :
32 : bool empty() const { return pc_offset_map_.empty(); }
33 :
34 : private:
35 : // pc_offset -> source line
36 : typedef std::map<int, int> PcOffsetMap;
37 : PcOffsetMap pc_offset_map_;
38 : DISALLOW_COPY_AND_ASSIGN(JITLineInfoTable);
39 : };
40 :
41 :
42 : class CodeEntry {
43 : public:
44 : // CodeEntry doesn't own name strings, just references them.
45 : inline CodeEntry(CodeEventListener::LogEventsAndTags tag, const char* name,
46 : const char* name_prefix = CodeEntry::kEmptyNamePrefix,
47 : const char* resource_name = CodeEntry::kEmptyResourceName,
48 : int line_number = v8::CpuProfileNode::kNoLineNumberInfo,
49 : int column_number = v8::CpuProfileNode::kNoColumnNumberInfo,
50 : JITLineInfoTable* line_info = nullptr,
51 : Address instruction_start = nullptr);
52 : ~CodeEntry();
53 :
54 : const char* name_prefix() const { return name_prefix_; }
55 382 : bool has_name_prefix() const { return name_prefix_[0] != '\0'; }
56 : const char* name() const { return name_; }
57 : const char* resource_name() const { return resource_name_; }
58 : int line_number() const { return line_number_; }
59 : int column_number() const { return column_number_; }
60 : const JITLineInfoTable* line_info() const { return line_info_; }
61 : int script_id() const { return script_id_; }
62 1528 : void set_script_id(int script_id) { script_id_ = script_id; }
63 : int position() const { return position_; }
64 1528 : void set_position(int position) { position_ = position; }
65 8 : void set_bailout_reason(const char* bailout_reason) {
66 1536 : bailout_reason_ = bailout_reason;
67 8 : }
68 : const char* bailout_reason() const { return bailout_reason_; }
69 :
70 9 : void set_deopt_info(const char* deopt_reason, int deopt_id) {
71 : DCHECK(!has_deopt_info());
72 9 : deopt_reason_ = deopt_reason;
73 9 : deopt_id_ = deopt_id;
74 9 : }
75 : CpuProfileDeoptInfo GetDeoptInfo();
76 : bool has_deopt_info() const { return deopt_id_ != kNoDeoptimizationId; }
77 : void clear_deopt_info() {
78 6 : deopt_reason_ = kNoDeoptReason;
79 6 : deopt_id_ = kNoDeoptimizationId;
80 : }
81 :
82 : void FillFunctionInfo(SharedFunctionInfo* shared);
83 :
84 : void SetBuiltinId(Builtins::Name id);
85 : Builtins::Name builtin_id() const {
86 : return BuiltinIdField::decode(bit_field_);
87 : }
88 :
89 : uint32_t GetHash() const;
90 : bool IsSameFunctionAs(CodeEntry* entry) const;
91 :
92 : int GetSourceLine(int pc_offset) const;
93 :
94 : void AddInlineStack(int pc_offset, std::vector<CodeEntry*> inline_stack);
95 : const std::vector<CodeEntry*>* GetInlineStack(int pc_offset) const;
96 :
97 : void AddDeoptInlinedFrames(int deopt_id, std::vector<CpuProfileDeoptFrame>);
98 : bool HasDeoptInlinedFramesFor(int deopt_id) const;
99 :
100 : Address instruction_start() const { return instruction_start_; }
101 : CodeEventListener::LogEventsAndTags tag() const {
102 : return TagField::decode(bit_field_);
103 : }
104 :
105 : static const char* const kEmptyNamePrefix;
106 : static const char* const kEmptyResourceName;
107 : static const char* const kEmptyBailoutReason;
108 : static const char* const kNoDeoptReason;
109 :
110 : static const char* const kProgramEntryName;
111 : static const char* const kIdleEntryName;
112 : static const char* const kGarbageCollectorEntryName;
113 : // Used to represent frames for which we have no reliable way to
114 : // detect function.
115 : static const char* const kUnresolvedFunctionName;
116 :
117 : V8_INLINE static CodeEntry* program_entry() {
118 1807 : return kProgramEntry.Pointer();
119 : }
120 15 : V8_INLINE static CodeEntry* idle_entry() { return kIdleEntry.Pointer(); }
121 785 : V8_INLINE static CodeEntry* gc_entry() { return kGCEntry.Pointer(); }
122 : V8_INLINE static CodeEntry* unresolved_entry() {
123 1 : return kUnresolvedEntry.Pointer();
124 : }
125 :
126 : private:
127 : struct ProgramEntryCreateTrait {
128 : static CodeEntry* Create();
129 : };
130 : struct IdleEntryCreateTrait {
131 : static CodeEntry* Create();
132 : };
133 : struct GCEntryCreateTrait {
134 : static CodeEntry* Create();
135 : };
136 : struct UnresolvedEntryCreateTrait {
137 : static CodeEntry* Create();
138 : };
139 :
140 : static base::LazyDynamicInstance<CodeEntry, ProgramEntryCreateTrait>::type
141 : kProgramEntry;
142 : static base::LazyDynamicInstance<CodeEntry, IdleEntryCreateTrait>::type
143 : kIdleEntry;
144 : static base::LazyDynamicInstance<CodeEntry, GCEntryCreateTrait>::type
145 : kGCEntry;
146 : static base::LazyDynamicInstance<CodeEntry, UnresolvedEntryCreateTrait>::type
147 : kUnresolvedEntry;
148 :
149 : class TagField : public BitField<Logger::LogEventsAndTags, 0, 8> {};
150 : class BuiltinIdField : public BitField<Builtins::Name, 8, 24> {};
151 :
152 : uint32_t bit_field_;
153 : const char* name_prefix_;
154 : const char* name_;
155 : const char* resource_name_;
156 : int line_number_;
157 : int column_number_;
158 : int script_id_;
159 : int position_;
160 : const char* bailout_reason_;
161 : const char* deopt_reason_;
162 : int deopt_id_;
163 : JITLineInfoTable* line_info_;
164 : Address instruction_start_;
165 : // Should be an unordered_map, but it doesn't currently work on Win & MacOS.
166 : std::map<int, std::vector<CodeEntry*>> inline_locations_;
167 : std::map<int, std::vector<CpuProfileDeoptFrame>> deopt_inlined_frames_;
168 :
169 : DISALLOW_COPY_AND_ASSIGN(CodeEntry);
170 : };
171 :
172 :
173 : class ProfileTree;
174 :
175 6198 : class ProfileNode {
176 : public:
177 : inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent);
178 :
179 : ProfileNode* FindChild(CodeEntry* entry);
180 : ProfileNode* FindOrAddChild(CodeEntry* entry);
181 32323 : void IncrementSelfTicks() { ++self_ticks_; }
182 : void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; }
183 : void IncrementLineTicks(int src_line);
184 :
185 : CodeEntry* entry() const { return entry_; }
186 : unsigned self_ticks() const { return self_ticks_; }
187 9525 : const std::vector<ProfileNode*>* children() const { return &children_list_; }
188 : unsigned id() const { return id_; }
189 : unsigned function_id() const;
190 : ProfileNode* parent() const { return parent_; }
191 103 : unsigned int GetHitLineCount() const { return line_ticks_.occupancy(); }
192 : bool GetLineTicks(v8::CpuProfileNode::LineTick* entries,
193 : unsigned int length) const;
194 : void CollectDeoptInfo(CodeEntry* entry);
195 : const std::vector<CpuProfileDeoptInfo>& deopt_infos() const {
196 : return deopt_infos_;
197 : }
198 : Isolate* isolate() const;
199 :
200 : void Print(int indent);
201 :
202 78751 : static bool CodeEntriesMatch(void* entry1, void* entry2) {
203 : return reinterpret_cast<CodeEntry*>(entry1)
204 78751 : ->IsSameFunctionAs(reinterpret_cast<CodeEntry*>(entry2));
205 : }
206 :
207 : private:
208 80989 : static uint32_t CodeEntryHash(CodeEntry* entry) { return entry->GetHash(); }
209 :
210 29545 : static bool LineTickMatch(void* a, void* b) { return a == b; }
211 :
212 : ProfileTree* tree_;
213 : CodeEntry* entry_;
214 : unsigned self_ticks_;
215 : // Mapping from CodeEntry* to ProfileNode*
216 : base::CustomMatcherHashMap children_;
217 : std::vector<ProfileNode*> children_list_;
218 : ProfileNode* parent_;
219 : unsigned id_;
220 : base::CustomMatcherHashMap line_ticks_;
221 :
222 : std::vector<CpuProfileDeoptInfo> deopt_infos_;
223 :
224 : DISALLOW_COPY_AND_ASSIGN(ProfileNode);
225 : };
226 :
227 :
228 : class ProfileTree {
229 : public:
230 : explicit ProfileTree(Isolate* isolate);
231 : ~ProfileTree();
232 :
233 : ProfileNode* AddPathFromEnd(
234 : const std::vector<CodeEntry*>& path,
235 : int src_line = v8::CpuProfileNode::kNoLineNumberInfo,
236 : bool update_stats = true);
237 : ProfileNode* root() const { return root_; }
238 3099 : unsigned next_node_id() { return next_node_id_++; }
239 : unsigned GetFunctionId(const ProfileNode* node);
240 :
241 : void Print() {
242 59 : root_->Print(0);
243 : }
244 :
245 : Isolate* isolate() const { return isolate_; }
246 :
247 3099 : void EnqueueNode(const ProfileNode* node) { pending_nodes_.push_back(node); }
248 32434 : size_t pending_nodes_count() const { return pending_nodes_.size(); }
249 : std::vector<const ProfileNode*> TakePendingNodes() {
250 : return std::move(pending_nodes_);
251 : }
252 :
253 : private:
254 : template <typename Callback>
255 : void TraverseDepthFirst(Callback* callback);
256 :
257 : std::vector<const ProfileNode*> pending_nodes_;
258 :
259 : CodeEntry root_entry_;
260 : unsigned next_node_id_;
261 : ProfileNode* root_;
262 : Isolate* isolate_;
263 :
264 : unsigned next_function_id_;
265 : base::CustomMatcherHashMap function_ids_;
266 :
267 : DISALLOW_COPY_AND_ASSIGN(ProfileTree);
268 : };
269 :
270 :
271 1830 : class CpuProfile {
272 : public:
273 : CpuProfile(CpuProfiler* profiler, const char* title, bool record_samples);
274 :
275 : // Add pc -> ... -> main() call path to the profile.
276 : void AddPath(base::TimeTicks timestamp, const std::vector<CodeEntry*>& path,
277 : int src_line, bool update_stats);
278 : void FinishProfile();
279 :
280 : const char* title() const { return title_; }
281 : const ProfileTree* top_down() const { return &top_down_; }
282 :
283 16004 : int samples_count() const { return static_cast<int>(samples_.size()); }
284 16012 : ProfileNode* sample(int index) const { return samples_.at(index); }
285 7988 : base::TimeTicks sample_timestamp(int index) const {
286 15976 : return timestamps_.at(index);
287 : }
288 :
289 : base::TimeTicks start_time() const { return start_time_; }
290 : base::TimeTicks end_time() const { return end_time_; }
291 : CpuProfiler* cpu_profiler() const { return profiler_; }
292 :
293 : void UpdateTicksScale();
294 :
295 : void Print();
296 :
297 : private:
298 : void StreamPendingTraceEvents();
299 :
300 : const char* title_;
301 : bool record_samples_;
302 : base::TimeTicks start_time_;
303 : base::TimeTicks end_time_;
304 : std::vector<ProfileNode*> samples_;
305 : std::vector<base::TimeTicks> timestamps_;
306 : ProfileTree top_down_;
307 : CpuProfiler* const profiler_;
308 : size_t streaming_next_sample_;
309 :
310 : DISALLOW_COPY_AND_ASSIGN(CpuProfile);
311 : };
312 :
313 : class CodeMap {
314 : public:
315 : CodeMap() {}
316 :
317 : void AddCode(Address addr, CodeEntry* entry, unsigned size);
318 : void MoveCode(Address from, Address to);
319 : CodeEntry* FindEntry(Address addr);
320 : void Print();
321 :
322 : private:
323 : struct CodeEntryInfo {
324 : CodeEntryInfo(CodeEntry* an_entry, unsigned a_size)
325 : : entry(an_entry), size(a_size) { }
326 : CodeEntry* entry;
327 : unsigned size;
328 : };
329 :
330 : void DeleteAllCoveredCode(Address start, Address end);
331 :
332 : std::map<Address, CodeEntryInfo> code_map_;
333 :
334 : DISALLOW_COPY_AND_ASSIGN(CodeMap);
335 : };
336 :
337 : class CpuProfilesCollection {
338 : public:
339 : explicit CpuProfilesCollection(Isolate* isolate);
340 : ~CpuProfilesCollection();
341 :
342 108863 : void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; }
343 : bool StartProfiling(const char* title, bool record_samples);
344 : CpuProfile* StopProfiling(const char* title);
345 : std::vector<CpuProfile*>* profiles() { return &finished_profiles_; }
346 392 : const char* GetName(Name* name) { return resource_names_.GetName(name); }
347 : bool IsLastProfile(const char* title);
348 : void RemoveProfile(CpuProfile* profile);
349 :
350 : // Called from profile generator thread.
351 : void AddPathToCurrentProfiles(base::TimeTicks timestamp,
352 : const std::vector<CodeEntry*>& path,
353 : int src_line, bool update_stats);
354 :
355 : // Limits the number of profiles that can be simultaneously collected.
356 : static const int kMaxSimultaneousProfiles = 100;
357 :
358 : private:
359 : StringsStorage resource_names_;
360 : std::vector<CpuProfile*> finished_profiles_;
361 : CpuProfiler* profiler_;
362 :
363 : // Accessed by VM thread and profile generator thread.
364 : std::vector<CpuProfile*> current_profiles_;
365 : base::Semaphore current_profiles_semaphore_;
366 :
367 : DISALLOW_COPY_AND_ASSIGN(CpuProfilesCollection);
368 : };
369 :
370 :
371 : class ProfileGenerator {
372 : public:
373 : explicit ProfileGenerator(CpuProfilesCollection* profiles);
374 :
375 : void RecordTickSample(const TickSample& sample);
376 :
377 : CodeMap* code_map() { return &code_map_; }
378 :
379 : private:
380 : CodeEntry* FindEntry(void* address);
381 : CodeEntry* EntryForVMState(StateTag tag);
382 :
383 : CpuProfilesCollection* profiles_;
384 : CodeMap code_map_;
385 :
386 : DISALLOW_COPY_AND_ASSIGN(ProfileGenerator);
387 : };
388 :
389 :
390 : } // namespace internal
391 : } // namespace v8
392 :
393 : #endif // V8_PROFILER_PROFILE_GENERATOR_H_
|