Home | History | Annotate | Download | only in profiler
      1 // Copyright 2015 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_SAMPLING_HEAP_PROFILER_H_
      6 #define V8_PROFILER_SAMPLING_HEAP_PROFILER_H_
      7 
      8 #include <deque>
      9 #include <map>
     10 #include <memory>
     11 #include <set>
     12 #include "include/v8-profiler.h"
     13 #include "src/heap/heap.h"
     14 #include "src/profiler/strings-storage.h"
     15 
     16 namespace v8 {
     17 
     18 namespace base {
     19 class RandomNumberGenerator;
     20 }
     21 
     22 namespace internal {
     23 
     24 class SamplingAllocationObserver;
     25 
     26 class AllocationProfile : public v8::AllocationProfile {
     27  public:
     28   AllocationProfile() : nodes_() {}
     29 
     30   v8::AllocationProfile::Node* GetRootNode() override {
     31     return nodes_.size() == 0 ? nullptr : &nodes_.front();
     32   }
     33 
     34   std::deque<v8::AllocationProfile::Node>& nodes() { return nodes_; }
     35 
     36  private:
     37   std::deque<v8::AllocationProfile::Node> nodes_;
     38 
     39   DISALLOW_COPY_AND_ASSIGN(AllocationProfile);
     40 };
     41 
     42 class SamplingHeapProfiler {
     43  public:
     44   SamplingHeapProfiler(Heap* heap, StringsStorage* names, uint64_t rate,
     45                        int stack_depth, v8::HeapProfiler::SamplingFlags flags);
     46   ~SamplingHeapProfiler();
     47 
     48   v8::AllocationProfile* GetAllocationProfile();
     49 
     50   StringsStorage* names() const { return names_; }
     51 
     52   class AllocationNode;
     53 
     54   struct Sample {
     55    public:
     56     Sample(size_t size_, AllocationNode* owner_, Local<Value> local_,
     57            SamplingHeapProfiler* profiler_)
     58         : size(size_),
     59           owner(owner_),
     60           global(Global<Value>(
     61               reinterpret_cast<v8::Isolate*>(profiler_->isolate_), local_)),
     62           profiler(profiler_) {}
     63     ~Sample() { global.Reset(); }
     64     const size_t size;
     65     AllocationNode* const owner;
     66     Global<Value> global;
     67     SamplingHeapProfiler* const profiler;
     68 
     69    private:
     70     DISALLOW_COPY_AND_ASSIGN(Sample);
     71   };
     72 
     73   class AllocationNode {
     74    public:
     75     AllocationNode(AllocationNode* parent, const char* name, int script_id,
     76                    int start_position)
     77         : parent_(parent),
     78           script_id_(script_id),
     79           script_position_(start_position),
     80           name_(name),
     81           pinned_(false) {}
     82     ~AllocationNode() {
     83       for (auto child : children_) {
     84         delete child.second;
     85       }
     86     }
     87 
     88    private:
     89     typedef uint64_t FunctionId;
     90     static FunctionId function_id(int script_id, int start_position,
     91                                   const char* name) {
     92       // script_id == kNoScriptId case:
     93       //   Use function name pointer as an id. Names derived from VM state
     94       //   must not collide with the builtin names. The least significant bit
     95       //   of the id is set to 1.
     96       if (script_id == v8::UnboundScript::kNoScriptId) {
     97         return reinterpret_cast<intptr_t>(name) | 1;
     98       }
     99       // script_id != kNoScriptId case:
    100       //   Use script_id, start_position pair to uniquelly identify the node.
    101       //   The least significant bit of the id is set to 0.
    102       DCHECK(static_cast<unsigned>(start_position) < (1u << 31));
    103       return (static_cast<uint64_t>(script_id) << 32) + (start_position << 1);
    104     }
    105     AllocationNode* FindOrAddChildNode(const char* name, int script_id,
    106                                        int start_position);
    107     // TODO(alph): make use of unordered_map's here. Pay attention to
    108     // iterator invalidation during TranslateAllocationNode.
    109     std::map<size_t, unsigned int> allocations_;
    110     std::map<FunctionId, AllocationNode*> children_;
    111     AllocationNode* const parent_;
    112     const int script_id_;
    113     const int script_position_;
    114     const char* const name_;
    115     bool pinned_;
    116 
    117     friend class SamplingHeapProfiler;
    118 
    119     DISALLOW_COPY_AND_ASSIGN(AllocationNode);
    120   };
    121 
    122  private:
    123   Heap* heap() const { return heap_; }
    124 
    125   void SampleObject(Address soon_object, size_t size);
    126 
    127   static void OnWeakCallback(const WeakCallbackInfo<Sample>& data);
    128 
    129   // Methods that construct v8::AllocationProfile.
    130 
    131   // Translates the provided AllocationNode *node* returning an equivalent
    132   // AllocationProfile::Node. The newly created AllocationProfile::Node is added
    133   // to the provided AllocationProfile *profile*. Line numbers, column numbers,
    134   // and script names are resolved using *scripts* which maps all currently
    135   // loaded scripts keyed by their script id.
    136   v8::AllocationProfile::Node* TranslateAllocationNode(
    137       AllocationProfile* profile, SamplingHeapProfiler::AllocationNode* node,
    138       const std::map<int, Handle<Script>>& scripts);
    139   v8::AllocationProfile::Allocation ScaleSample(size_t size,
    140                                                 unsigned int count);
    141   AllocationNode* AddStack();
    142 
    143   Isolate* const isolate_;
    144   Heap* const heap_;
    145   std::unique_ptr<SamplingAllocationObserver> new_space_observer_;
    146   std::unique_ptr<SamplingAllocationObserver> other_spaces_observer_;
    147   StringsStorage* const names_;
    148   AllocationNode profile_root_;
    149   std::set<Sample*> samples_;
    150   const int stack_depth_;
    151   const uint64_t rate_;
    152   v8::HeapProfiler::SamplingFlags flags_;
    153 
    154   friend class SamplingAllocationObserver;
    155 
    156   DISALLOW_COPY_AND_ASSIGN(SamplingHeapProfiler);
    157 };
    158 
    159 class SamplingAllocationObserver : public AllocationObserver {
    160  public:
    161   SamplingAllocationObserver(Heap* heap, intptr_t step_size, uint64_t rate,
    162                              SamplingHeapProfiler* profiler,
    163                              base::RandomNumberGenerator* random)
    164       : AllocationObserver(step_size),
    165         profiler_(profiler),
    166         heap_(heap),
    167         random_(random),
    168         rate_(rate) {}
    169   virtual ~SamplingAllocationObserver() {}
    170 
    171  protected:
    172   void Step(int bytes_allocated, Address soon_object, size_t size) override {
    173     USE(heap_);
    174     DCHECK(heap_->gc_state() == Heap::NOT_IN_GC);
    175     DCHECK(soon_object);
    176     profiler_->SampleObject(soon_object, size);
    177   }
    178 
    179   intptr_t GetNextStepSize() override { return GetNextSampleInterval(rate_); }
    180 
    181  private:
    182   intptr_t GetNextSampleInterval(uint64_t rate);
    183   SamplingHeapProfiler* const profiler_;
    184   Heap* const heap_;
    185   base::RandomNumberGenerator* const random_;
    186   uint64_t const rate_;
    187 };
    188 
    189 }  // namespace internal
    190 }  // namespace v8
    191 
    192 #endif  // V8_PROFILER_SAMPLING_HEAP_PROFILER_H_
    193