1 // Copyright 2009 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #ifndef V8_HEAP_PROFILER_H_ 29 #define V8_HEAP_PROFILER_H_ 30 31 namespace v8 { 32 namespace internal { 33 34 #ifdef ENABLE_LOGGING_AND_PROFILING 35 36 // The HeapProfiler writes data to the log files, which can be postprocessed 37 // to generate .hp files for use by the GHC/Valgrind tool hp2ps. 38 class HeapProfiler { 39 public: 40 // Write a single heap sample to the log file. 41 static void WriteSample(); 42 43 private: 44 // Update the array info with stats from obj. 45 static void CollectStats(HeapObject* obj, HistogramInfo* info); 46 }; 47 48 49 // JSObjectsCluster describes a group of JS objects that are 50 // considered equivalent in terms of a particular profile. 51 class JSObjectsCluster BASE_EMBEDDED { 52 public: 53 // These special cases are used in retainer profile. 54 enum SpecialCase { 55 ROOTS = 1, 56 GLOBAL_PROPERTY = 2, 57 CODE = 3, 58 SELF = 100 // This case is used in ClustersCoarser only. 59 }; 60 61 JSObjectsCluster() : constructor_(NULL), instance_(NULL) {} 62 explicit JSObjectsCluster(String* constructor) 63 : constructor_(constructor), instance_(NULL) {} 64 explicit JSObjectsCluster(SpecialCase special) 65 : constructor_(FromSpecialCase(special)), instance_(NULL) {} 66 JSObjectsCluster(String* constructor, Object* instance) 67 : constructor_(constructor), instance_(instance) {} 68 69 static int CompareConstructors(const JSObjectsCluster& a, 70 const JSObjectsCluster& b) { 71 // Strings are unique, so it is sufficient to compare their pointers. 72 return a.constructor_ == b.constructor_ ? 0 73 : (a.constructor_ < b.constructor_ ? -1 : 1); 74 } 75 static int Compare(const JSObjectsCluster& a, const JSObjectsCluster& b) { 76 // Strings are unique, so it is sufficient to compare their pointers. 77 const int cons_cmp = CompareConstructors(a, b); 78 return cons_cmp == 0 ? 79 (a.instance_ == b.instance_ ? 0 : (a.instance_ < b.instance_ ? -1 : 1)) 80 : cons_cmp; 81 } 82 static int Compare(const JSObjectsCluster* a, const JSObjectsCluster* b) { 83 return Compare(*a, *b); 84 } 85 86 bool is_null() const { return constructor_ == NULL; } 87 bool can_be_coarsed() const { return instance_ != NULL; } 88 String* constructor() const { return constructor_; } 89 90 void Print(StringStream* accumulator) const; 91 // Allows null clusters to be printed. 92 void DebugPrint(StringStream* accumulator) const; 93 94 private: 95 static String* FromSpecialCase(SpecialCase special) { 96 // We use symbols that are illegal JS identifiers to identify special cases. 97 // Their actual value is irrelevant for us. 98 switch (special) { 99 case ROOTS: return Heap::result_symbol(); 100 case GLOBAL_PROPERTY: return Heap::code_symbol(); 101 case CODE: return Heap::arguments_shadow_symbol(); 102 case SELF: return Heap::catch_var_symbol(); 103 default: 104 UNREACHABLE(); 105 return NULL; 106 } 107 } 108 109 String* constructor_; 110 Object* instance_; 111 }; 112 113 114 struct JSObjectsClusterTreeConfig { 115 typedef JSObjectsCluster Key; 116 typedef NumberAndSizeInfo Value; 117 static const Key kNoKey; 118 static const Value kNoValue; 119 static int Compare(const Key& a, const Key& b) { 120 return Key::Compare(a, b); 121 } 122 }; 123 typedef ZoneSplayTree<JSObjectsClusterTreeConfig> JSObjectsClusterTree; 124 125 126 // ConstructorHeapProfile is responsible for gathering and logging 127 // "constructor profile" of JS objects allocated on heap. 128 // It is run during garbage collection cycle, thus it doesn't need 129 // to use handles. 130 class ConstructorHeapProfile BASE_EMBEDDED { 131 public: 132 ConstructorHeapProfile(); 133 virtual ~ConstructorHeapProfile() {} 134 void CollectStats(HeapObject* obj); 135 void PrintStats(); 136 // Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in tests. 137 virtual void Call(const JSObjectsCluster& cluster, 138 const NumberAndSizeInfo& number_and_size); 139 140 private: 141 ZoneScope zscope_; 142 JSObjectsClusterTree js_objects_info_tree_; 143 }; 144 145 146 // JSObjectsRetainerTree is used to represent retainer graphs using 147 // adjacency list form: 148 // 149 // Cluster -> (Cluster -> NumberAndSizeInfo) 150 // 151 // Subordinate splay trees are stored by pointer. They are zone-allocated, 152 // so it isn't needed to manage their lifetime. 153 // 154 struct JSObjectsRetainerTreeConfig { 155 typedef JSObjectsCluster Key; 156 typedef JSObjectsClusterTree* Value; 157 static const Key kNoKey; 158 static const Value kNoValue; 159 static int Compare(const Key& a, const Key& b) { 160 return Key::Compare(a, b); 161 } 162 }; 163 typedef ZoneSplayTree<JSObjectsRetainerTreeConfig> JSObjectsRetainerTree; 164 165 166 class ClustersCoarser BASE_EMBEDDED { 167 public: 168 ClustersCoarser(); 169 170 // Processes a given retainer graph. 171 void Process(JSObjectsRetainerTree* tree); 172 173 // Returns an equivalent cluster (can be the cluster itself). 174 // If the given cluster doesn't have an equivalent, returns null cluster. 175 JSObjectsCluster GetCoarseEquivalent(const JSObjectsCluster& cluster); 176 // Returns whether a cluster can be substitued with an equivalent and thus, 177 // skipped in some cases. 178 bool HasAnEquivalent(const JSObjectsCluster& cluster); 179 180 // Used by JSObjectsRetainerTree::ForEach. 181 void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree); 182 void Call(const JSObjectsCluster& cluster, 183 const NumberAndSizeInfo& number_and_size); 184 185 private: 186 // Stores a list of back references for a cluster. 187 struct ClusterBackRefs { 188 explicit ClusterBackRefs(const JSObjectsCluster& cluster_); 189 ClusterBackRefs(const ClusterBackRefs& src); 190 ClusterBackRefs& operator=(const ClusterBackRefs& src); 191 192 static int Compare(const ClusterBackRefs& a, const ClusterBackRefs& b); 193 void SortRefs() { refs.Sort(JSObjectsCluster::Compare); } 194 static void SortRefsIterator(ClusterBackRefs* ref) { ref->SortRefs(); } 195 196 JSObjectsCluster cluster; 197 ZoneList<JSObjectsCluster> refs; 198 }; 199 typedef ZoneList<ClusterBackRefs> SimilarityList; 200 201 // A tree for storing a list of equivalents for a cluster. 202 struct ClusterEqualityConfig { 203 typedef JSObjectsCluster Key; 204 typedef JSObjectsCluster Value; 205 static const Key kNoKey; 206 static const Value kNoValue; 207 static int Compare(const Key& a, const Key& b) { 208 return Key::Compare(a, b); 209 } 210 }; 211 typedef ZoneSplayTree<ClusterEqualityConfig> EqualityTree; 212 213 static int ClusterBackRefsCmp(const ClusterBackRefs* a, 214 const ClusterBackRefs* b) { 215 return ClusterBackRefs::Compare(*a, *b); 216 } 217 int DoProcess(JSObjectsRetainerTree* tree); 218 int FillEqualityTree(); 219 220 static const int kInitialBackrefsListCapacity = 2; 221 static const int kInitialSimilarityListCapacity = 2000; 222 // Number of passes for finding equivalents. Limits the length of paths 223 // that can be considered equivalent. 224 static const int kMaxPassesCount = 10; 225 226 ZoneScope zscope_; 227 SimilarityList sim_list_; 228 EqualityTree eq_tree_; 229 ClusterBackRefs* current_pair_; 230 JSObjectsRetainerTree* current_set_; 231 const JSObjectsCluster* self_; 232 }; 233 234 235 // RetainerHeapProfile is responsible for gathering and logging 236 // "retainer profile" of JS objects allocated on heap. 237 // It is run during garbage collection cycle, thus it doesn't need 238 // to use handles. 239 class RetainerHeapProfile BASE_EMBEDDED { 240 public: 241 class Printer { 242 public: 243 virtual ~Printer() {} 244 virtual void PrintRetainers(const JSObjectsCluster& cluster, 245 const StringStream& retainers) = 0; 246 }; 247 248 RetainerHeapProfile(); 249 void CollectStats(HeapObject* obj); 250 void PrintStats(); 251 void DebugPrintStats(Printer* printer); 252 void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref); 253 254 private: 255 ZoneScope zscope_; 256 JSObjectsRetainerTree retainers_tree_; 257 ClustersCoarser coarser_; 258 }; 259 260 261 class ProducerHeapProfile : public AllStatic { 262 public: 263 static void Setup(); 264 static void RecordJSObjectAllocation(Object* obj) { 265 if (FLAG_log_producers) DoRecordJSObjectAllocation(obj); 266 } 267 268 private: 269 static void DoRecordJSObjectAllocation(Object* obj); 270 static bool can_log_; 271 }; 272 273 #endif // ENABLE_LOGGING_AND_PROFILING 274 275 } } // namespace v8::internal 276 277 #endif // V8_HEAP_PROFILER_H_ 278