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