blob: 20ba457c5bf911b0248439181bf054713f58ef83 [file] [log] [blame]
Ben Murdochb0fe1622011-05-05 13:52:32 +01001// Copyright 2009-2010 the V8 project authors. All rights reserved.
Steve Blocka7e24c12009-10-30 11:49:00 +00002// 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
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010031#include "zone-inl.h"
Steve Block6ded16b2010-05-10 14:33:55 +010032
Steve Blocka7e24c12009-10-30 11:49:00 +000033namespace v8 {
34namespace internal {
35
36#ifdef ENABLE_LOGGING_AND_PROFILING
37
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010038class HeapSnapshot;
39class HeapSnapshotsCollection;
40
Ben Murdoch3bec4d22010-07-22 14:51:16 +010041#define HEAP_PROFILE(Call) \
42 do { \
43 if (v8::internal::HeapProfiler::is_profiling()) { \
44 v8::internal::HeapProfiler::Call; \
45 } \
46 } while (false)
47#else
48#define HEAP_PROFILE(Call) ((void) 0)
49#endif // ENABLE_LOGGING_AND_PROFILING
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010050
Steve Blocka7e24c12009-10-30 11:49:00 +000051// The HeapProfiler writes data to the log files, which can be postprocessed
52// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
53class HeapProfiler {
54 public:
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010055 static void Setup();
56 static void TearDown();
57
58#ifdef ENABLE_LOGGING_AND_PROFILING
Ben Murdochb0fe1622011-05-05 13:52:32 +010059 static HeapSnapshot* TakeSnapshot(const char* name,
60 int type,
61 v8::ActivityControl* control);
62 static HeapSnapshot* TakeSnapshot(String* name,
63 int type,
64 v8::ActivityControl* control);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010065 static int GetSnapshotsCount();
66 static HeapSnapshot* GetSnapshot(int index);
67 static HeapSnapshot* FindSnapshot(unsigned uid);
68
Ben Murdoch3bec4d22010-07-22 14:51:16 +010069 static void ObjectMoveEvent(Address from, Address to);
70
71 static INLINE(bool is_profiling()) {
72 return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects();
73 }
74
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010075 // Obsolete interface.
Steve Blocka7e24c12009-10-30 11:49:00 +000076 // Write a single heap sample to the log file.
77 static void WriteSample();
78
79 private:
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010080 HeapProfiler();
81 ~HeapProfiler();
Ben Murdochb0fe1622011-05-05 13:52:32 +010082 HeapSnapshot* TakeSnapshotImpl(const char* name,
83 int type,
84 v8::ActivityControl* control);
85 HeapSnapshot* TakeSnapshotImpl(String* name,
86 int type,
87 v8::ActivityControl* control);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010088
89 HeapSnapshotsCollection* snapshots_;
90 unsigned next_snapshot_uid_;
91
92 static HeapProfiler* singleton_;
93#endif // ENABLE_LOGGING_AND_PROFILING
Steve Blocka7e24c12009-10-30 11:49:00 +000094};
95
96
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010097#ifdef ENABLE_LOGGING_AND_PROFILING
98
Steve Blocka7e24c12009-10-30 11:49:00 +000099// JSObjectsCluster describes a group of JS objects that are
100// considered equivalent in terms of a particular profile.
101class JSObjectsCluster BASE_EMBEDDED {
102 public:
103 // These special cases are used in retainer profile.
104 enum SpecialCase {
105 ROOTS = 1,
106 GLOBAL_PROPERTY = 2,
Steve Blockd0582a62009-12-15 09:54:21 +0000107 CODE = 3,
108 SELF = 100 // This case is used in ClustersCoarser only.
Steve Blocka7e24c12009-10-30 11:49:00 +0000109 };
110
111 JSObjectsCluster() : constructor_(NULL), instance_(NULL) {}
112 explicit JSObjectsCluster(String* constructor)
113 : constructor_(constructor), instance_(NULL) {}
114 explicit JSObjectsCluster(SpecialCase special)
115 : constructor_(FromSpecialCase(special)), instance_(NULL) {}
116 JSObjectsCluster(String* constructor, Object* instance)
117 : constructor_(constructor), instance_(instance) {}
118
119 static int CompareConstructors(const JSObjectsCluster& a,
120 const JSObjectsCluster& b) {
121 // Strings are unique, so it is sufficient to compare their pointers.
122 return a.constructor_ == b.constructor_ ? 0
123 : (a.constructor_ < b.constructor_ ? -1 : 1);
124 }
125 static int Compare(const JSObjectsCluster& a, const JSObjectsCluster& b) {
126 // Strings are unique, so it is sufficient to compare their pointers.
127 const int cons_cmp = CompareConstructors(a, b);
128 return cons_cmp == 0 ?
129 (a.instance_ == b.instance_ ? 0 : (a.instance_ < b.instance_ ? -1 : 1))
130 : cons_cmp;
131 }
132 static int Compare(const JSObjectsCluster* a, const JSObjectsCluster* b) {
133 return Compare(*a, *b);
134 }
135
136 bool is_null() const { return constructor_ == NULL; }
137 bool can_be_coarsed() const { return instance_ != NULL; }
138 String* constructor() const { return constructor_; }
Steve Block791712a2010-08-27 10:21:07 +0100139 Object* instance() const { return instance_; }
Steve Blocka7e24c12009-10-30 11:49:00 +0000140
Steve Block791712a2010-08-27 10:21:07 +0100141 const char* GetSpecialCaseName() const;
Steve Blocka7e24c12009-10-30 11:49:00 +0000142 void Print(StringStream* accumulator) const;
143 // Allows null clusters to be printed.
144 void DebugPrint(StringStream* accumulator) const;
145
146 private:
147 static String* FromSpecialCase(SpecialCase special) {
148 // We use symbols that are illegal JS identifiers to identify special cases.
149 // Their actual value is irrelevant for us.
150 switch (special) {
151 case ROOTS: return Heap::result_symbol();
152 case GLOBAL_PROPERTY: return Heap::code_symbol();
Steve Blockd0582a62009-12-15 09:54:21 +0000153 case CODE: return Heap::arguments_shadow_symbol();
Steve Blocka7e24c12009-10-30 11:49:00 +0000154 case SELF: return Heap::catch_var_symbol();
155 default:
156 UNREACHABLE();
157 return NULL;
158 }
159 }
160
161 String* constructor_;
162 Object* instance_;
163};
164
165
166struct JSObjectsClusterTreeConfig {
167 typedef JSObjectsCluster Key;
168 typedef NumberAndSizeInfo Value;
169 static const Key kNoKey;
170 static const Value kNoValue;
171 static int Compare(const Key& a, const Key& b) {
172 return Key::Compare(a, b);
173 }
174};
175typedef ZoneSplayTree<JSObjectsClusterTreeConfig> JSObjectsClusterTree;
176
177
178// ConstructorHeapProfile is responsible for gathering and logging
179// "constructor profile" of JS objects allocated on heap.
180// It is run during garbage collection cycle, thus it doesn't need
181// to use handles.
182class ConstructorHeapProfile BASE_EMBEDDED {
183 public:
184 ConstructorHeapProfile();
185 virtual ~ConstructorHeapProfile() {}
186 void CollectStats(HeapObject* obj);
187 void PrintStats();
Steve Block791712a2010-08-27 10:21:07 +0100188
189 template<class Callback>
190 void ForEach(Callback* callback) { js_objects_info_tree_.ForEach(callback); }
Steve Blocka7e24c12009-10-30 11:49:00 +0000191 // Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in tests.
192 virtual void Call(const JSObjectsCluster& cluster,
193 const NumberAndSizeInfo& number_and_size);
194
195 private:
196 ZoneScope zscope_;
197 JSObjectsClusterTree js_objects_info_tree_;
198};
199
200
201// JSObjectsRetainerTree is used to represent retainer graphs using
202// adjacency list form:
203//
204// Cluster -> (Cluster -> NumberAndSizeInfo)
205//
206// Subordinate splay trees are stored by pointer. They are zone-allocated,
207// so it isn't needed to manage their lifetime.
208//
209struct JSObjectsRetainerTreeConfig {
210 typedef JSObjectsCluster Key;
211 typedef JSObjectsClusterTree* Value;
212 static const Key kNoKey;
213 static const Value kNoValue;
214 static int Compare(const Key& a, const Key& b) {
215 return Key::Compare(a, b);
216 }
217};
218typedef ZoneSplayTree<JSObjectsRetainerTreeConfig> JSObjectsRetainerTree;
219
220
221class ClustersCoarser BASE_EMBEDDED {
222 public:
223 ClustersCoarser();
224
225 // Processes a given retainer graph.
226 void Process(JSObjectsRetainerTree* tree);
227
228 // Returns an equivalent cluster (can be the cluster itself).
229 // If the given cluster doesn't have an equivalent, returns null cluster.
230 JSObjectsCluster GetCoarseEquivalent(const JSObjectsCluster& cluster);
231 // Returns whether a cluster can be substitued with an equivalent and thus,
232 // skipped in some cases.
233 bool HasAnEquivalent(const JSObjectsCluster& cluster);
234
235 // Used by JSObjectsRetainerTree::ForEach.
236 void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
237 void Call(const JSObjectsCluster& cluster,
238 const NumberAndSizeInfo& number_and_size);
239
240 private:
241 // Stores a list of back references for a cluster.
242 struct ClusterBackRefs {
243 explicit ClusterBackRefs(const JSObjectsCluster& cluster_);
244 ClusterBackRefs(const ClusterBackRefs& src);
245 ClusterBackRefs& operator=(const ClusterBackRefs& src);
246
247 static int Compare(const ClusterBackRefs& a, const ClusterBackRefs& b);
248 void SortRefs() { refs.Sort(JSObjectsCluster::Compare); }
249 static void SortRefsIterator(ClusterBackRefs* ref) { ref->SortRefs(); }
250
251 JSObjectsCluster cluster;
252 ZoneList<JSObjectsCluster> refs;
253 };
254 typedef ZoneList<ClusterBackRefs> SimilarityList;
255
256 // A tree for storing a list of equivalents for a cluster.
257 struct ClusterEqualityConfig {
258 typedef JSObjectsCluster Key;
259 typedef JSObjectsCluster Value;
260 static const Key kNoKey;
261 static const Value kNoValue;
262 static int Compare(const Key& a, const Key& b) {
263 return Key::Compare(a, b);
264 }
265 };
266 typedef ZoneSplayTree<ClusterEqualityConfig> EqualityTree;
267
268 static int ClusterBackRefsCmp(const ClusterBackRefs* a,
269 const ClusterBackRefs* b) {
270 return ClusterBackRefs::Compare(*a, *b);
271 }
272 int DoProcess(JSObjectsRetainerTree* tree);
273 int FillEqualityTree();
274
275 static const int kInitialBackrefsListCapacity = 2;
276 static const int kInitialSimilarityListCapacity = 2000;
277 // Number of passes for finding equivalents. Limits the length of paths
278 // that can be considered equivalent.
279 static const int kMaxPassesCount = 10;
280
281 ZoneScope zscope_;
282 SimilarityList sim_list_;
283 EqualityTree eq_tree_;
284 ClusterBackRefs* current_pair_;
285 JSObjectsRetainerTree* current_set_;
286 const JSObjectsCluster* self_;
287};
288
289
290// RetainerHeapProfile is responsible for gathering and logging
291// "retainer profile" of JS objects allocated on heap.
292// It is run during garbage collection cycle, thus it doesn't need
293// to use handles.
Steve Block791712a2010-08-27 10:21:07 +0100294class RetainerTreeAggregator;
295
Steve Blocka7e24c12009-10-30 11:49:00 +0000296class RetainerHeapProfile BASE_EMBEDDED {
297 public:
298 class Printer {
299 public:
300 virtual ~Printer() {}
301 virtual void PrintRetainers(const JSObjectsCluster& cluster,
302 const StringStream& retainers) = 0;
303 };
304
305 RetainerHeapProfile();
Steve Block791712a2010-08-27 10:21:07 +0100306 ~RetainerHeapProfile();
307
308 RetainerTreeAggregator* aggregator() { return aggregator_; }
309 ClustersCoarser* coarser() { return &coarser_; }
310 JSObjectsRetainerTree* retainers_tree() { return &retainers_tree_; }
311
Steve Blocka7e24c12009-10-30 11:49:00 +0000312 void CollectStats(HeapObject* obj);
Steve Block791712a2010-08-27 10:21:07 +0100313 void CoarseAndAggregate();
Steve Blocka7e24c12009-10-30 11:49:00 +0000314 void PrintStats();
315 void DebugPrintStats(Printer* printer);
316 void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref);
317
318 private:
319 ZoneScope zscope_;
320 JSObjectsRetainerTree retainers_tree_;
321 ClustersCoarser coarser_;
Steve Block791712a2010-08-27 10:21:07 +0100322 RetainerTreeAggregator* aggregator_;
323};
324
325
326class AggregatedHeapSnapshot {
327 public:
328 AggregatedHeapSnapshot();
329 ~AggregatedHeapSnapshot();
330
331 HistogramInfo* info() { return info_; }
332 ConstructorHeapProfile* js_cons_profile() { return &js_cons_profile_; }
333 RetainerHeapProfile* js_retainer_profile() { return &js_retainer_profile_; }
334
335 private:
336 HistogramInfo* info_;
337 ConstructorHeapProfile js_cons_profile_;
338 RetainerHeapProfile js_retainer_profile_;
339};
340
341
342class HeapEntriesMap;
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100343class HeapEntriesAllocator;
Steve Block791712a2010-08-27 10:21:07 +0100344class HeapSnapshot;
345
346class AggregatedHeapSnapshotGenerator {
347 public:
348 explicit AggregatedHeapSnapshotGenerator(AggregatedHeapSnapshot* snapshot);
349 void GenerateSnapshot();
350 void FillHeapSnapshot(HeapSnapshot* snapshot);
351
352 static const int kAllStringsType = LAST_TYPE + 1;
353
354 private:
355 void CalculateStringsStats();
356 void CollectStats(HeapObject* obj);
357 template<class Iterator>
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100358 void IterateRetainers(
359 HeapEntriesAllocator* allocator, HeapEntriesMap* entries_map);
Steve Block791712a2010-08-27 10:21:07 +0100360
361 AggregatedHeapSnapshot* agg_snapshot_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000362};
363
364
Steve Block3ce2e202009-11-05 08:53:23 +0000365class ProducerHeapProfile : public AllStatic {
366 public:
367 static void Setup();
Leon Clarkee46be812010-01-19 14:06:41 +0000368 static void RecordJSObjectAllocation(Object* obj) {
369 if (FLAG_log_producers) DoRecordJSObjectAllocation(obj);
370 }
371
Steve Block3ce2e202009-11-05 08:53:23 +0000372 private:
Leon Clarkee46be812010-01-19 14:06:41 +0000373 static void DoRecordJSObjectAllocation(Object* obj);
Steve Block3ce2e202009-11-05 08:53:23 +0000374 static bool can_log_;
375};
376
Steve Blocka7e24c12009-10-30 11:49:00 +0000377#endif // ENABLE_LOGGING_AND_PROFILING
378
379} } // namespace v8::internal
380
381#endif // V8_HEAP_PROFILER_H_