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