LCOV - code coverage report
Current view: top level - src/profiler - profile-generator.h (source / functions) Hit Total Coverage
Test: app.info Lines: 40 41 97.6 %
Date: 2019-04-18 Functions: 6 6 100.0 %

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

Generated by: LCOV version 1.10