blob: 2ef081ee29973b4572932517674f9179a17732cf [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// 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
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
Steve Block791712a2010-08-27 10:21:07 +010059 static HeapSnapshot* TakeSnapshot(const char* name, int type);
60 static HeapSnapshot* TakeSnapshot(String* name, int type);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010061 static int GetSnapshotsCount();
62 static HeapSnapshot* GetSnapshot(int index);
63 static HeapSnapshot* FindSnapshot(unsigned uid);
64
Ben Murdoch3bec4d22010-07-22 14:51:16 +010065 static void ObjectMoveEvent(Address from, Address to);
66
67 static INLINE(bool is_profiling()) {
68 return singleton_ != NULL && singleton_->snapshots_->is_tracking_objects();
69 }
70
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010071 // Obsolete interface.
Steve Blocka7e24c12009-10-30 11:49:00 +000072 // Write a single heap sample to the log file.
73 static void WriteSample();
74
75 private:
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010076 HeapProfiler();
77 ~HeapProfiler();
Steve Block791712a2010-08-27 10:21:07 +010078 HeapSnapshot* TakeSnapshotImpl(const char* name, int type);
79 HeapSnapshot* TakeSnapshotImpl(String* name, int type);
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010080
81 HeapSnapshotsCollection* snapshots_;
82 unsigned next_snapshot_uid_;
83
84 static HeapProfiler* singleton_;
85#endif // ENABLE_LOGGING_AND_PROFILING
Steve Blocka7e24c12009-10-30 11:49:00 +000086};
87
88
Kristian Monsen9dcf7e22010-06-28 14:14:28 +010089#ifdef ENABLE_LOGGING_AND_PROFILING
90
Steve Blocka7e24c12009-10-30 11:49:00 +000091// JSObjectsCluster describes a group of JS objects that are
92// considered equivalent in terms of a particular profile.
93class JSObjectsCluster BASE_EMBEDDED {
94 public:
95 // These special cases are used in retainer profile.
96 enum SpecialCase {
97 ROOTS = 1,
98 GLOBAL_PROPERTY = 2,
Steve Blockd0582a62009-12-15 09:54:21 +000099 CODE = 3,
100 SELF = 100 // This case is used in ClustersCoarser only.
Steve Blocka7e24c12009-10-30 11:49:00 +0000101 };
102
103 JSObjectsCluster() : constructor_(NULL), instance_(NULL) {}
104 explicit JSObjectsCluster(String* constructor)
105 : constructor_(constructor), instance_(NULL) {}
106 explicit JSObjectsCluster(SpecialCase special)
107 : constructor_(FromSpecialCase(special)), instance_(NULL) {}
108 JSObjectsCluster(String* constructor, Object* instance)
109 : constructor_(constructor), instance_(instance) {}
110
111 static int CompareConstructors(const JSObjectsCluster& a,
112 const JSObjectsCluster& b) {
113 // Strings are unique, so it is sufficient to compare their pointers.
114 return a.constructor_ == b.constructor_ ? 0
115 : (a.constructor_ < b.constructor_ ? -1 : 1);
116 }
117 static int Compare(const JSObjectsCluster& a, const JSObjectsCluster& b) {
118 // Strings are unique, so it is sufficient to compare their pointers.
119 const int cons_cmp = CompareConstructors(a, b);
120 return cons_cmp == 0 ?
121 (a.instance_ == b.instance_ ? 0 : (a.instance_ < b.instance_ ? -1 : 1))
122 : cons_cmp;
123 }
124 static int Compare(const JSObjectsCluster* a, const JSObjectsCluster* b) {
125 return Compare(*a, *b);
126 }
127
128 bool is_null() const { return constructor_ == NULL; }
129 bool can_be_coarsed() const { return instance_ != NULL; }
130 String* constructor() const { return constructor_; }
Steve Block791712a2010-08-27 10:21:07 +0100131 Object* instance() const { return instance_; }
Steve Blocka7e24c12009-10-30 11:49:00 +0000132
Steve Block791712a2010-08-27 10:21:07 +0100133 const char* GetSpecialCaseName() const;
Steve Blocka7e24c12009-10-30 11:49:00 +0000134 void Print(StringStream* accumulator) const;
135 // Allows null clusters to be printed.
136 void DebugPrint(StringStream* accumulator) const;
137
138 private:
139 static String* FromSpecialCase(SpecialCase special) {
140 // We use symbols that are illegal JS identifiers to identify special cases.
141 // Their actual value is irrelevant for us.
142 switch (special) {
143 case ROOTS: return Heap::result_symbol();
144 case GLOBAL_PROPERTY: return Heap::code_symbol();
Steve Blockd0582a62009-12-15 09:54:21 +0000145 case CODE: return Heap::arguments_shadow_symbol();
Steve Blocka7e24c12009-10-30 11:49:00 +0000146 case SELF: return Heap::catch_var_symbol();
147 default:
148 UNREACHABLE();
149 return NULL;
150 }
151 }
152
153 String* constructor_;
154 Object* instance_;
155};
156
157
158struct JSObjectsClusterTreeConfig {
159 typedef JSObjectsCluster Key;
160 typedef NumberAndSizeInfo Value;
161 static const Key kNoKey;
162 static const Value kNoValue;
163 static int Compare(const Key& a, const Key& b) {
164 return Key::Compare(a, b);
165 }
166};
167typedef ZoneSplayTree<JSObjectsClusterTreeConfig> JSObjectsClusterTree;
168
169
170// ConstructorHeapProfile is responsible for gathering and logging
171// "constructor profile" of JS objects allocated on heap.
172// It is run during garbage collection cycle, thus it doesn't need
173// to use handles.
174class ConstructorHeapProfile BASE_EMBEDDED {
175 public:
176 ConstructorHeapProfile();
177 virtual ~ConstructorHeapProfile() {}
178 void CollectStats(HeapObject* obj);
179 void PrintStats();
Steve Block791712a2010-08-27 10:21:07 +0100180
181 template<class Callback>
182 void ForEach(Callback* callback) { js_objects_info_tree_.ForEach(callback); }
Steve Blocka7e24c12009-10-30 11:49:00 +0000183 // Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in tests.
184 virtual void Call(const JSObjectsCluster& cluster,
185 const NumberAndSizeInfo& number_and_size);
186
187 private:
188 ZoneScope zscope_;
189 JSObjectsClusterTree js_objects_info_tree_;
190};
191
192
193// JSObjectsRetainerTree is used to represent retainer graphs using
194// adjacency list form:
195//
196// Cluster -> (Cluster -> NumberAndSizeInfo)
197//
198// Subordinate splay trees are stored by pointer. They are zone-allocated,
199// so it isn't needed to manage their lifetime.
200//
201struct JSObjectsRetainerTreeConfig {
202 typedef JSObjectsCluster Key;
203 typedef JSObjectsClusterTree* Value;
204 static const Key kNoKey;
205 static const Value kNoValue;
206 static int Compare(const Key& a, const Key& b) {
207 return Key::Compare(a, b);
208 }
209};
210typedef ZoneSplayTree<JSObjectsRetainerTreeConfig> JSObjectsRetainerTree;
211
212
213class ClustersCoarser BASE_EMBEDDED {
214 public:
215 ClustersCoarser();
216
217 // Processes a given retainer graph.
218 void Process(JSObjectsRetainerTree* tree);
219
220 // Returns an equivalent cluster (can be the cluster itself).
221 // If the given cluster doesn't have an equivalent, returns null cluster.
222 JSObjectsCluster GetCoarseEquivalent(const JSObjectsCluster& cluster);
223 // Returns whether a cluster can be substitued with an equivalent and thus,
224 // skipped in some cases.
225 bool HasAnEquivalent(const JSObjectsCluster& cluster);
226
227 // Used by JSObjectsRetainerTree::ForEach.
228 void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
229 void Call(const JSObjectsCluster& cluster,
230 const NumberAndSizeInfo& number_and_size);
231
232 private:
233 // Stores a list of back references for a cluster.
234 struct ClusterBackRefs {
235 explicit ClusterBackRefs(const JSObjectsCluster& cluster_);
236 ClusterBackRefs(const ClusterBackRefs& src);
237 ClusterBackRefs& operator=(const ClusterBackRefs& src);
238
239 static int Compare(const ClusterBackRefs& a, const ClusterBackRefs& b);
240 void SortRefs() { refs.Sort(JSObjectsCluster::Compare); }
241 static void SortRefsIterator(ClusterBackRefs* ref) { ref->SortRefs(); }
242
243 JSObjectsCluster cluster;
244 ZoneList<JSObjectsCluster> refs;
245 };
246 typedef ZoneList<ClusterBackRefs> SimilarityList;
247
248 // A tree for storing a list of equivalents for a cluster.
249 struct ClusterEqualityConfig {
250 typedef JSObjectsCluster Key;
251 typedef JSObjectsCluster Value;
252 static const Key kNoKey;
253 static const Value kNoValue;
254 static int Compare(const Key& a, const Key& b) {
255 return Key::Compare(a, b);
256 }
257 };
258 typedef ZoneSplayTree<ClusterEqualityConfig> EqualityTree;
259
260 static int ClusterBackRefsCmp(const ClusterBackRefs* a,
261 const ClusterBackRefs* b) {
262 return ClusterBackRefs::Compare(*a, *b);
263 }
264 int DoProcess(JSObjectsRetainerTree* tree);
265 int FillEqualityTree();
266
267 static const int kInitialBackrefsListCapacity = 2;
268 static const int kInitialSimilarityListCapacity = 2000;
269 // Number of passes for finding equivalents. Limits the length of paths
270 // that can be considered equivalent.
271 static const int kMaxPassesCount = 10;
272
273 ZoneScope zscope_;
274 SimilarityList sim_list_;
275 EqualityTree eq_tree_;
276 ClusterBackRefs* current_pair_;
277 JSObjectsRetainerTree* current_set_;
278 const JSObjectsCluster* self_;
279};
280
281
282// RetainerHeapProfile is responsible for gathering and logging
283// "retainer profile" of JS objects allocated on heap.
284// It is run during garbage collection cycle, thus it doesn't need
285// to use handles.
Steve Block791712a2010-08-27 10:21:07 +0100286class RetainerTreeAggregator;
287
Steve Blocka7e24c12009-10-30 11:49:00 +0000288class RetainerHeapProfile BASE_EMBEDDED {
289 public:
290 class Printer {
291 public:
292 virtual ~Printer() {}
293 virtual void PrintRetainers(const JSObjectsCluster& cluster,
294 const StringStream& retainers) = 0;
295 };
296
297 RetainerHeapProfile();
Steve Block791712a2010-08-27 10:21:07 +0100298 ~RetainerHeapProfile();
299
300 RetainerTreeAggregator* aggregator() { return aggregator_; }
301 ClustersCoarser* coarser() { return &coarser_; }
302 JSObjectsRetainerTree* retainers_tree() { return &retainers_tree_; }
303
Steve Blocka7e24c12009-10-30 11:49:00 +0000304 void CollectStats(HeapObject* obj);
Steve Block791712a2010-08-27 10:21:07 +0100305 void CoarseAndAggregate();
Steve Blocka7e24c12009-10-30 11:49:00 +0000306 void PrintStats();
307 void DebugPrintStats(Printer* printer);
308 void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref);
309
310 private:
311 ZoneScope zscope_;
312 JSObjectsRetainerTree retainers_tree_;
313 ClustersCoarser coarser_;
Steve Block791712a2010-08-27 10:21:07 +0100314 RetainerTreeAggregator* aggregator_;
315};
316
317
318class AggregatedHeapSnapshot {
319 public:
320 AggregatedHeapSnapshot();
321 ~AggregatedHeapSnapshot();
322
323 HistogramInfo* info() { return info_; }
324 ConstructorHeapProfile* js_cons_profile() { return &js_cons_profile_; }
325 RetainerHeapProfile* js_retainer_profile() { return &js_retainer_profile_; }
326
327 private:
328 HistogramInfo* info_;
329 ConstructorHeapProfile js_cons_profile_;
330 RetainerHeapProfile js_retainer_profile_;
331};
332
333
334class HeapEntriesMap;
335class HeapSnapshot;
336
337class AggregatedHeapSnapshotGenerator {
338 public:
339 explicit AggregatedHeapSnapshotGenerator(AggregatedHeapSnapshot* snapshot);
340 void GenerateSnapshot();
341 void FillHeapSnapshot(HeapSnapshot* snapshot);
342
343 static const int kAllStringsType = LAST_TYPE + 1;
344
345 private:
346 void CalculateStringsStats();
347 void CollectStats(HeapObject* obj);
348 template<class Iterator>
349 void IterateRetainers(HeapEntriesMap* entries_map);
350
351 AggregatedHeapSnapshot* agg_snapshot_;
Steve Blocka7e24c12009-10-30 11:49:00 +0000352};
353
354
Steve Block3ce2e202009-11-05 08:53:23 +0000355class ProducerHeapProfile : public AllStatic {
356 public:
357 static void Setup();
Leon Clarkee46be812010-01-19 14:06:41 +0000358 static void RecordJSObjectAllocation(Object* obj) {
359 if (FLAG_log_producers) DoRecordJSObjectAllocation(obj);
360 }
361
Steve Block3ce2e202009-11-05 08:53:23 +0000362 private:
Leon Clarkee46be812010-01-19 14:06:41 +0000363 static void DoRecordJSObjectAllocation(Object* obj);
Steve Block3ce2e202009-11-05 08:53:23 +0000364 static bool can_log_;
365};
366
Steve Blocka7e24c12009-10-30 11:49:00 +0000367#endif // ENABLE_LOGGING_AND_PROFILING
368
369} } // namespace v8::internal
370
371#endif // V8_HEAP_PROFILER_H_