blob: c1e93c09caabde6f22c994676a836a1d31d921ce [file] [log] [blame]
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001// Copyright 2009-2010 the V8 project authors. All rights reserved.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +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
lrn@chromium.org1c092762011-05-09 09:42:16 +000031#include "allocation.h"
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000032#include "isolate.h"
whesse@chromium.org2c186ca2010-06-16 11:32:39 +000033#include "zone-inl.h"
ager@chromium.orgce5e87b2010-03-10 10:24:18 +000034
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +000035namespace v8 {
36namespace internal {
37
38#ifdef ENABLE_LOGGING_AND_PROFILING
39
whesse@chromium.org2c186ca2010-06-16 11:32:39 +000040class HeapSnapshot;
41class HeapSnapshotsCollection;
42
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000043#define HEAP_PROFILE(heap, call) \
44 do { \
45 v8::internal::HeapProfiler* profiler = heap->isolate()->heap_profiler(); \
46 if (profiler != NULL && profiler->is_profiling()) { \
47 profiler->call; \
48 } \
ricow@chromium.org4980dff2010-07-19 08:33:45 +000049 } while (false)
50#else
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000051#define HEAP_PROFILE(heap, call) ((void) 0)
ricow@chromium.org4980dff2010-07-19 08:33:45 +000052#endif // ENABLE_LOGGING_AND_PROFILING
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000053
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +000054// The HeapProfiler writes data to the log files, which can be postprocessed
55// to generate .hp files for use by the GHC/Valgrind tool hp2ps.
56class HeapProfiler {
57 public:
whesse@chromium.org2c186ca2010-06-16 11:32:39 +000058 static void Setup();
59 static void TearDown();
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +000060
61#ifdef ENABLE_LOGGING_AND_PROFILING
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000062 static HeapSnapshot* TakeSnapshot(const char* name,
63 int type,
64 v8::ActivityControl* control);
65 static HeapSnapshot* TakeSnapshot(String* name,
66 int type,
67 v8::ActivityControl* control);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +000068 static int GetSnapshotsCount();
69 static HeapSnapshot* GetSnapshot(int index);
70 static HeapSnapshot* FindSnapshot(unsigned uid);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000071 static void DeleteAllSnapshots();
whesse@chromium.org2c186ca2010-06-16 11:32:39 +000072
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000073 void ObjectMoveEvent(Address from, Address to);
ricow@chromium.org4980dff2010-07-19 08:33:45 +000074
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000075 void DefineWrapperClass(
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000076 uint16_t class_id, v8::HeapProfiler::WrapperInfoCallback callback);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000077
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000078 v8::RetainedObjectInfo* ExecuteWrapperClassCallback(uint16_t class_id,
79 Object** wrapper);
80 INLINE(bool is_profiling()) {
81 return snapshots_->is_tracking_objects();
ricow@chromium.org4980dff2010-07-19 08:33:45 +000082 }
83
whesse@chromium.org2c186ca2010-06-16 11:32:39 +000084 // Obsolete interface.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +000085 // Write a single heap sample to the log file.
86 static void WriteSample();
87
88 private:
whesse@chromium.org2c186ca2010-06-16 11:32:39 +000089 HeapProfiler();
90 ~HeapProfiler();
ager@chromium.org5f0c45f2010-12-17 08:51:21 +000091 HeapSnapshot* TakeSnapshotImpl(const char* name,
92 int type,
93 v8::ActivityControl* control);
94 HeapSnapshot* TakeSnapshotImpl(String* name,
95 int type,
96 v8::ActivityControl* control);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000097 void ResetSnapshots();
whesse@chromium.org2c186ca2010-06-16 11:32:39 +000098
99 HeapSnapshotsCollection* snapshots_;
100 unsigned next_snapshot_uid_;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000101 List<v8::HeapProfiler::WrapperInfoCallback> wrapper_callbacks_;
whesse@chromium.org2c186ca2010-06-16 11:32:39 +0000102
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000103#endif // ENABLE_LOGGING_AND_PROFILING
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000104};
105
106
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000107#ifdef ENABLE_LOGGING_AND_PROFILING
108
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000109// JSObjectsCluster describes a group of JS objects that are
110// considered equivalent in terms of a particular profile.
111class JSObjectsCluster BASE_EMBEDDED {
112 public:
113 // These special cases are used in retainer profile.
114 enum SpecialCase {
115 ROOTS = 1,
mikhail.naganov@gmail.com8578ca62009-09-30 14:20:18 +0000116 GLOBAL_PROPERTY = 2,
ager@chromium.org3811b432009-10-28 14:53:37 +0000117 CODE = 3,
118 SELF = 100 // This case is used in ClustersCoarser only.
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000119 };
120
121 JSObjectsCluster() : constructor_(NULL), instance_(NULL) {}
122 explicit JSObjectsCluster(String* constructor)
123 : constructor_(constructor), instance_(NULL) {}
124 explicit JSObjectsCluster(SpecialCase special)
125 : constructor_(FromSpecialCase(special)), instance_(NULL) {}
126 JSObjectsCluster(String* constructor, Object* instance)
127 : constructor_(constructor), instance_(instance) {}
128
129 static int CompareConstructors(const JSObjectsCluster& a,
130 const JSObjectsCluster& b) {
131 // Strings are unique, so it is sufficient to compare their pointers.
132 return a.constructor_ == b.constructor_ ? 0
133 : (a.constructor_ < b.constructor_ ? -1 : 1);
134 }
135 static int Compare(const JSObjectsCluster& a, const JSObjectsCluster& b) {
136 // Strings are unique, so it is sufficient to compare their pointers.
137 const int cons_cmp = CompareConstructors(a, b);
138 return cons_cmp == 0 ?
139 (a.instance_ == b.instance_ ? 0 : (a.instance_ < b.instance_ ? -1 : 1))
140 : cons_cmp;
141 }
mikhail.naganov@gmail.com8578ca62009-09-30 14:20:18 +0000142 static int Compare(const JSObjectsCluster* a, const JSObjectsCluster* b) {
143 return Compare(*a, *b);
144 }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000145
146 bool is_null() const { return constructor_ == NULL; }
147 bool can_be_coarsed() const { return instance_ != NULL; }
148 String* constructor() const { return constructor_; }
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000149 Object* instance() const { return instance_; }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000150
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000151 const char* GetSpecialCaseName() const;
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000152 void Print(StringStream* accumulator) const;
153 // Allows null clusters to be printed.
154 void DebugPrint(StringStream* accumulator) const;
155
156 private:
157 static String* FromSpecialCase(SpecialCase special) {
158 // We use symbols that are illegal JS identifiers to identify special cases.
159 // Their actual value is irrelevant for us.
160 switch (special) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000161 case ROOTS: return HEAP->result_symbol();
162 case GLOBAL_PROPERTY: return HEAP->code_symbol();
163 case CODE: return HEAP->arguments_shadow_symbol();
164 case SELF: return HEAP->catch_var_symbol();
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000165 default:
166 UNREACHABLE();
167 return NULL;
168 }
169 }
170
171 String* constructor_;
172 Object* instance_;
173};
174
175
176struct JSObjectsClusterTreeConfig {
177 typedef JSObjectsCluster Key;
178 typedef NumberAndSizeInfo Value;
179 static const Key kNoKey;
180 static const Value kNoValue;
181 static int Compare(const Key& a, const Key& b) {
182 return Key::Compare(a, b);
183 }
184};
185typedef ZoneSplayTree<JSObjectsClusterTreeConfig> JSObjectsClusterTree;
186
187
188// ConstructorHeapProfile is responsible for gathering and logging
189// "constructor profile" of JS objects allocated on heap.
190// It is run during garbage collection cycle, thus it doesn't need
191// to use handles.
192class ConstructorHeapProfile BASE_EMBEDDED {
193 public:
194 ConstructorHeapProfile();
195 virtual ~ConstructorHeapProfile() {}
196 void CollectStats(HeapObject* obj);
197 void PrintStats();
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000198
199 template<class Callback>
200 void ForEach(Callback* callback) { js_objects_info_tree_.ForEach(callback); }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000201 // Used by ZoneSplayTree::ForEach. Made virtual to allow overriding in tests.
202 virtual void Call(const JSObjectsCluster& cluster,
203 const NumberAndSizeInfo& number_and_size);
204
205 private:
206 ZoneScope zscope_;
207 JSObjectsClusterTree js_objects_info_tree_;
208};
209
210
211// JSObjectsRetainerTree is used to represent retainer graphs using
212// adjacency list form:
213//
214// Cluster -> (Cluster -> NumberAndSizeInfo)
215//
216// Subordinate splay trees are stored by pointer. They are zone-allocated,
217// so it isn't needed to manage their lifetime.
218//
219struct JSObjectsRetainerTreeConfig {
220 typedef JSObjectsCluster Key;
221 typedef JSObjectsClusterTree* Value;
222 static const Key kNoKey;
223 static const Value kNoValue;
224 static int Compare(const Key& a, const Key& b) {
225 return Key::Compare(a, b);
226 }
227};
228typedef ZoneSplayTree<JSObjectsRetainerTreeConfig> JSObjectsRetainerTree;
229
230
231class ClustersCoarser BASE_EMBEDDED {
232 public:
233 ClustersCoarser();
234
235 // Processes a given retainer graph.
236 void Process(JSObjectsRetainerTree* tree);
237
238 // Returns an equivalent cluster (can be the cluster itself).
239 // If the given cluster doesn't have an equivalent, returns null cluster.
240 JSObjectsCluster GetCoarseEquivalent(const JSObjectsCluster& cluster);
241 // Returns whether a cluster can be substitued with an equivalent and thus,
242 // skipped in some cases.
243 bool HasAnEquivalent(const JSObjectsCluster& cluster);
244
245 // Used by JSObjectsRetainerTree::ForEach.
246 void Call(const JSObjectsCluster& cluster, JSObjectsClusterTree* tree);
247 void Call(const JSObjectsCluster& cluster,
248 const NumberAndSizeInfo& number_and_size);
249
250 private:
251 // Stores a list of back references for a cluster.
252 struct ClusterBackRefs {
253 explicit ClusterBackRefs(const JSObjectsCluster& cluster_);
254 ClusterBackRefs(const ClusterBackRefs& src);
255 ClusterBackRefs& operator=(const ClusterBackRefs& src);
256
257 static int Compare(const ClusterBackRefs& a, const ClusterBackRefs& b);
mikhail.naganov@gmail.com8578ca62009-09-30 14:20:18 +0000258 void SortRefs() { refs.Sort(JSObjectsCluster::Compare); }
259 static void SortRefsIterator(ClusterBackRefs* ref) { ref->SortRefs(); }
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000260
261 JSObjectsCluster cluster;
262 ZoneList<JSObjectsCluster> refs;
263 };
264 typedef ZoneList<ClusterBackRefs> SimilarityList;
265
266 // A tree for storing a list of equivalents for a cluster.
267 struct ClusterEqualityConfig {
268 typedef JSObjectsCluster Key;
269 typedef JSObjectsCluster Value;
270 static const Key kNoKey;
271 static const Value kNoValue;
272 static int Compare(const Key& a, const Key& b) {
273 return Key::Compare(a, b);
274 }
275 };
276 typedef ZoneSplayTree<ClusterEqualityConfig> EqualityTree;
277
278 static int ClusterBackRefsCmp(const ClusterBackRefs* a,
279 const ClusterBackRefs* b) {
280 return ClusterBackRefs::Compare(*a, *b);
281 }
282 int DoProcess(JSObjectsRetainerTree* tree);
283 int FillEqualityTree();
284
285 static const int kInitialBackrefsListCapacity = 2;
286 static const int kInitialSimilarityListCapacity = 2000;
287 // Number of passes for finding equivalents. Limits the length of paths
288 // that can be considered equivalent.
289 static const int kMaxPassesCount = 10;
290
291 ZoneScope zscope_;
292 SimilarityList sim_list_;
293 EqualityTree eq_tree_;
294 ClusterBackRefs* current_pair_;
295 JSObjectsRetainerTree* current_set_;
mikhail.naganov@gmail.com8578ca62009-09-30 14:20:18 +0000296 const JSObjectsCluster* self_;
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000297};
298
299
300// RetainerHeapProfile is responsible for gathering and logging
301// "retainer profile" of JS objects allocated on heap.
302// It is run during garbage collection cycle, thus it doesn't need
303// to use handles.
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000304class RetainerTreeAggregator;
305
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000306class RetainerHeapProfile BASE_EMBEDDED {
307 public:
308 class Printer {
309 public:
310 virtual ~Printer() {}
311 virtual void PrintRetainers(const JSObjectsCluster& cluster,
312 const StringStream& retainers) = 0;
313 };
314
315 RetainerHeapProfile();
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000316 ~RetainerHeapProfile();
317
318 RetainerTreeAggregator* aggregator() { return aggregator_; }
319 ClustersCoarser* coarser() { return &coarser_; }
320 JSObjectsRetainerTree* retainers_tree() { return &retainers_tree_; }
321
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000322 void CollectStats(HeapObject* obj);
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000323 void CoarseAndAggregate();
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000324 void PrintStats();
325 void DebugPrintStats(Printer* printer);
326 void StoreReference(const JSObjectsCluster& cluster, HeapObject* ref);
327
328 private:
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000329 ZoneScope zscope_;
330 JSObjectsRetainerTree retainers_tree_;
331 ClustersCoarser coarser_;
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000332 RetainerTreeAggregator* aggregator_;
333};
334
335
336class AggregatedHeapSnapshot {
337 public:
338 AggregatedHeapSnapshot();
339 ~AggregatedHeapSnapshot();
340
341 HistogramInfo* info() { return info_; }
342 ConstructorHeapProfile* js_cons_profile() { return &js_cons_profile_; }
343 RetainerHeapProfile* js_retainer_profile() { return &js_retainer_profile_; }
344
345 private:
346 HistogramInfo* info_;
347 ConstructorHeapProfile js_cons_profile_;
348 RetainerHeapProfile js_retainer_profile_;
349};
350
351
352class HeapEntriesMap;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000353class HeapEntriesAllocator;
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000354
355class AggregatedHeapSnapshotGenerator {
356 public:
357 explicit AggregatedHeapSnapshotGenerator(AggregatedHeapSnapshot* snapshot);
358 void GenerateSnapshot();
359 void FillHeapSnapshot(HeapSnapshot* snapshot);
360
361 static const int kAllStringsType = LAST_TYPE + 1;
362
363 private:
364 void CalculateStringsStats();
365 void CollectStats(HeapObject* obj);
366 template<class Iterator>
ager@chromium.org9ee27ae2011-03-02 13:43:26 +0000367 void IterateRetainers(
368 HeapEntriesAllocator* allocator, HeapEntriesMap* entries_map);
erik.corry@gmail.com145eff52010-08-23 11:36:18 +0000369
370 AggregatedHeapSnapshot* agg_snapshot_;
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000371};
372
373
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000374class ProducerHeapProfile {
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000375 public:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000376 void Setup();
377 void RecordJSObjectAllocation(Object* obj) {
sgjesse@chromium.org846fb742009-12-18 08:56:33 +0000378 if (FLAG_log_producers) DoRecordJSObjectAllocation(obj);
379 }
380
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000381 private:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000382 ProducerHeapProfile() : can_log_(false) { }
383
384 void DoRecordJSObjectAllocation(Object* obj);
385 Isolate* isolate_;
386 bool can_log_;
387
388 friend class Isolate;
389
390 DISALLOW_COPY_AND_ASSIGN(ProducerHeapProfile);
christian.plesner.hansen@gmail.com9d58c2b2009-10-16 11:48:38 +0000391};
392
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000393#endif // ENABLE_LOGGING_AND_PROFILING
394
395} } // namespace v8::internal
396
397#endif // V8_HEAP_PROFILER_H_