• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
empty()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 
name_prefix()52   const char* name_prefix() const { return name_prefix_; }
has_name_prefix()53   bool has_name_prefix() const { return name_prefix_[0] != '\0'; }
name()54   const char* name() const { return name_; }
resource_name()55   const char* resource_name() const { return resource_name_; }
line_number()56   int line_number() const { return line_number_; }
column_number()57   int column_number() const { return column_number_; }
line_info()58   const JITLineInfoTable* line_info() const { return line_info_; }
script_id()59   int script_id() const { return script_id_; }
set_script_id(int script_id)60   void set_script_id(int script_id) { script_id_ = script_id; }
position()61   int position() const { return position_; }
set_position(int position)62   void set_position(int position) { position_ = position; }
set_bailout_reason(const char * bailout_reason)63   void set_bailout_reason(const char* bailout_reason) {
64     bailout_reason_ = bailout_reason;
65   }
bailout_reason()66   const char* bailout_reason() const { return bailout_reason_; }
67 
set_deopt_info(const char * deopt_reason,int deopt_id)68   void set_deopt_info(const char* deopt_reason, int deopt_id) {
69     DCHECK(!has_deopt_info());
70     deopt_reason_ = deopt_reason;
71     deopt_id_ = deopt_id;
72   }
73   CpuProfileDeoptInfo GetDeoptInfo();
has_deopt_info()74   bool has_deopt_info() const { return deopt_id_ != kNoDeoptimizationId; }
clear_deopt_info()75   void clear_deopt_info() {
76     deopt_reason_ = kNoDeoptReason;
77     deopt_id_ = kNoDeoptimizationId;
78   }
79 
80   void FillFunctionInfo(SharedFunctionInfo* shared);
81 
82   void SetBuiltinId(Builtins::Name id);
builtin_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 
instruction_start()98   Address instruction_start() const { return instruction_start_; }
tag()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 
program_entry()115   V8_INLINE static CodeEntry* program_entry() {
116     return kProgramEntry.Pointer();
117   }
idle_entry()118   V8_INLINE static CodeEntry* idle_entry() { return kIdleEntry.Pointer(); }
gc_entry()119   V8_INLINE static CodeEntry* gc_entry() { return kGCEntry.Pointer(); }
unresolved_entry()120   V8_INLINE static CodeEntry* unresolved_entry() {
121     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 class ProfileNode {
174  public:
175   inline ProfileNode(ProfileTree* tree, CodeEntry* entry, ProfileNode* parent);
176 
177   ProfileNode* FindChild(CodeEntry* entry);
178   ProfileNode* FindOrAddChild(CodeEntry* entry);
IncrementSelfTicks()179   void IncrementSelfTicks() { ++self_ticks_; }
IncreaseSelfTicks(unsigned amount)180   void IncreaseSelfTicks(unsigned amount) { self_ticks_ += amount; }
181   void IncrementLineTicks(int src_line);
182 
entry()183   CodeEntry* entry() const { return entry_; }
self_ticks()184   unsigned self_ticks() const { return self_ticks_; }
children()185   const List<ProfileNode*>* children() const { return &children_list_; }
id()186   unsigned id() const { return id_; }
187   unsigned function_id() const;
parent()188   ProfileNode* parent() const { return parent_; }
GetHitLineCount()189   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);
deopt_infos()193   const std::vector<CpuProfileDeoptInfo>& deopt_infos() const {
194     return deopt_infos_;
195   }
196   Isolate* isolate() const;
197 
198   void Print(int indent);
199 
CodeEntriesMatch(void * entry1,void * entry2)200   static bool CodeEntriesMatch(void* entry1, void* entry2) {
201     return reinterpret_cast<CodeEntry*>(entry1)
202         ->IsSameFunctionAs(reinterpret_cast<CodeEntry*>(entry2));
203   }
204 
205  private:
CodeEntryHash(CodeEntry * entry)206   static uint32_t CodeEntryHash(CodeEntry* entry) { return entry->GetHash(); }
207 
LineTickMatch(void * a,void * b)208   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);
root()235   ProfileNode* root() const { return root_; }
next_node_id()236   unsigned next_node_id() { return next_node_id_++; }
237   unsigned GetFunctionId(const ProfileNode* node);
238 
Print()239   void Print() {
240     root_->Print(0);
241   }
242 
isolate()243   Isolate* isolate() const { return isolate_; }
244 
EnqueueNode(const ProfileNode * node)245   void EnqueueNode(const ProfileNode* node) { pending_nodes_.push_back(node); }
pending_nodes_count()246   size_t pending_nodes_count() const { return pending_nodes_.size(); }
TakePendingNodes()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 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 
title()278   const char* title() const { return title_; }
top_down()279   const ProfileTree* top_down() const { return &top_down_; }
280 
samples_count()281   int samples_count() const { return samples_.length(); }
sample(int index)282   ProfileNode* sample(int index) const { return samples_.at(index); }
sample_timestamp(int index)283   base::TimeTicks sample_timestamp(int index) const {
284     return timestamps_.at(index);
285   }
286 
start_time()287   base::TimeTicks start_time() const { return start_time_; }
end_time()288   base::TimeTicks end_time() const { return end_time_; }
cpu_profiler()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:
CodeMap()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 {
CodeEntryInfoCodeEntryInfo322     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 
set_cpu_profiler(CpuProfiler * profiler)340   void set_cpu_profiler(CpuProfiler* profiler) { profiler_ = profiler; }
341   bool StartProfiling(const char* title, bool record_samples);
342   CpuProfile* StopProfiling(const char* title);
profiles()343   List<CpuProfile*>* profiles() { return &finished_profiles_; }
GetName(Name * name)344   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 
code_map()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_
392