1 | /* |
2 | * Copyright (C) 2012 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "HeapStatistics.h" |
28 | |
29 | #include "Heap.h" |
30 | #include "HeapIterationScope.h" |
31 | #include "JSCInlines.h" |
32 | #include "JSObject.h" |
33 | #include "Options.h" |
34 | #include <stdlib.h> |
35 | #include <wtf/CurrentTime.h> |
36 | #include <wtf/DataLog.h> |
37 | #include <wtf/StdLibExtras.h> |
38 | |
39 | #if OS(UNIX) |
40 | #include <sys/resource.h> |
41 | #endif |
42 | |
43 | namespace JSC { |
44 | |
45 | double HeapStatistics::s_startTime = 0.0; |
46 | double HeapStatistics::s_endTime = 0.0; |
47 | Vector<double>* HeapStatistics::s_pauseTimeStarts = 0; |
48 | Vector<double>* HeapStatistics::s_pauseTimeEnds = 0; |
49 | |
50 | #if OS(UNIX) |
51 | |
52 | void HeapStatistics::initialize() |
53 | { |
54 | ASSERT(Options::recordGCPauseTimes()); |
55 | s_startTime = WTF::monotonicallyIncreasingTime(); |
56 | s_pauseTimeStarts = new Vector<double>(); |
57 | s_pauseTimeEnds = new Vector<double>(); |
58 | } |
59 | |
60 | void HeapStatistics::recordGCPauseTime(double start, double end) |
61 | { |
62 | ASSERT(Options::recordGCPauseTimes()); |
63 | ASSERT(s_pauseTimeStarts); |
64 | ASSERT(s_pauseTimeEnds); |
65 | s_pauseTimeStarts->append(start); |
66 | s_pauseTimeEnds->append(end); |
67 | } |
68 | |
69 | void HeapStatistics::logStatistics() |
70 | { |
71 | struct rusage usage; |
72 | getrusage(RUSAGE_SELF, &usage); |
73 | #if USE(CF) || OS(UNIX) |
74 | char* vmName = getenv("JSVMName" ); |
75 | char* suiteName = getenv("JSSuiteName" ); |
76 | char* benchmarkName = getenv("JSBenchmarkName" ); |
77 | #else |
78 | #error "The HeapStatistics module is not supported on this platform." |
79 | #endif |
80 | if (!vmName || !suiteName || !benchmarkName) |
81 | dataLogF("HeapStatistics: {\"max_rss\": %ld" , usage.ru_maxrss); |
82 | else |
83 | dataLogF("HeapStatistics: {\"max_rss\": %ld, \"vm_name\": \"%s\", \"suite_name\": \"%s\", \"benchmark_name\": \"%s\"" , |
84 | usage.ru_maxrss, vmName, suiteName, benchmarkName); |
85 | |
86 | if (Options::recordGCPauseTimes()) { |
87 | dataLogF(", \"pause_times\": [" ); |
88 | Vector<double>::iterator startIt = s_pauseTimeStarts->begin(); |
89 | Vector<double>::iterator endIt = s_pauseTimeEnds->begin(); |
90 | if (startIt != s_pauseTimeStarts->end() && endIt != s_pauseTimeEnds->end()) { |
91 | dataLogF("[%f, %f]" , *startIt, *endIt); |
92 | ++startIt; |
93 | ++endIt; |
94 | } |
95 | while (startIt != s_pauseTimeStarts->end() && endIt != s_pauseTimeEnds->end()) { |
96 | dataLogF(", [%f, %f]" , *startIt, *endIt); |
97 | ++startIt; |
98 | ++endIt; |
99 | } |
100 | dataLogF("], \"start_time\": %f, \"end_time\": %f" , s_startTime, s_endTime); |
101 | } |
102 | dataLogF("}\n" ); |
103 | } |
104 | |
105 | void HeapStatistics::exitWithFailure() |
106 | { |
107 | ASSERT(Options::logHeapStatisticsAtExit()); |
108 | s_endTime = WTF::monotonicallyIncreasingTime(); |
109 | logStatistics(); |
110 | exit(-1); |
111 | } |
112 | |
113 | void HeapStatistics::reportSuccess() |
114 | { |
115 | ASSERT(Options::logHeapStatisticsAtExit()); |
116 | s_endTime = WTF::monotonicallyIncreasingTime(); |
117 | logStatistics(); |
118 | } |
119 | |
120 | #else |
121 | |
122 | void HeapStatistics::initialize() |
123 | { |
124 | } |
125 | |
126 | void HeapStatistics::recordGCPauseTime(double, double) |
127 | { |
128 | } |
129 | |
130 | void HeapStatistics::logStatistics() |
131 | { |
132 | } |
133 | |
134 | void HeapStatistics::exitWithFailure() |
135 | { |
136 | exit(-1); |
137 | } |
138 | |
139 | void HeapStatistics::reportSuccess() |
140 | { |
141 | } |
142 | |
143 | #endif // OS(UNIX) |
144 | |
145 | class StorageStatistics : public MarkedBlock::VoidFunctor { |
146 | public: |
147 | StorageStatistics(); |
148 | |
149 | IterationStatus operator()(JSCell*); |
150 | |
151 | size_t objectWithOutOfLineStorageCount(); |
152 | size_t objectCount(); |
153 | |
154 | size_t storageSize(); |
155 | size_t storageCapacity(); |
156 | |
157 | private: |
158 | void visit(JSCell*); |
159 | |
160 | size_t m_objectWithOutOfLineStorageCount; |
161 | size_t m_objectCount; |
162 | size_t m_storageSize; |
163 | size_t m_storageCapacity; |
164 | }; |
165 | |
166 | inline StorageStatistics::StorageStatistics() |
167 | : m_objectWithOutOfLineStorageCount(0) |
168 | , m_objectCount(0) |
169 | , m_storageSize(0) |
170 | , m_storageCapacity(0) |
171 | { |
172 | } |
173 | |
174 | inline void StorageStatistics::visit(JSCell* cell) |
175 | { |
176 | if (!cell->isObject()) |
177 | return; |
178 | |
179 | JSObject* object = jsCast<JSObject*>(cell); |
180 | if (hasIndexedProperties(object->indexingType())) |
181 | return; |
182 | |
183 | if (object->structure()->isUncacheableDictionary()) |
184 | return; |
185 | |
186 | ++m_objectCount; |
187 | if (!object->hasInlineStorage()) |
188 | ++m_objectWithOutOfLineStorageCount; |
189 | m_storageSize += object->structure()->totalStorageSize() * sizeof(WriteBarrierBase<Unknown>); |
190 | m_storageCapacity += object->structure()->totalStorageCapacity() * sizeof(WriteBarrierBase<Unknown>); |
191 | } |
192 | |
193 | inline IterationStatus StorageStatistics::operator()(JSCell* cell) |
194 | { |
195 | visit(cell); |
196 | return IterationStatus::Continue; |
197 | } |
198 | |
199 | inline size_t StorageStatistics::objectWithOutOfLineStorageCount() |
200 | { |
201 | return m_objectWithOutOfLineStorageCount; |
202 | } |
203 | |
204 | inline size_t StorageStatistics::objectCount() |
205 | { |
206 | return m_objectCount; |
207 | } |
208 | |
209 | inline size_t StorageStatistics::storageSize() |
210 | { |
211 | return m_storageSize; |
212 | } |
213 | |
214 | inline size_t StorageStatistics::storageCapacity() |
215 | { |
216 | return m_storageCapacity; |
217 | } |
218 | |
219 | void HeapStatistics::dumpObjectStatistics(Heap* heap) |
220 | { |
221 | dataLogF("\n=== Heap Statistics: ===\n" ); |
222 | dataLogF("size: %ldkB\n" , static_cast<long>(heap->m_sizeAfterLastCollect / KB)); |
223 | dataLogF("capacity: %ldkB\n" , static_cast<long>(heap->capacity() / KB)); |
224 | dataLogF("pause time: %lfs\n\n" , heap->m_lastFullGCLength); |
225 | |
226 | StorageStatistics storageStatistics; |
227 | { |
228 | HeapIterationScope iterationScope(*heap); |
229 | heap->m_objectSpace.forEachLiveCell(iterationScope, storageStatistics); |
230 | } |
231 | long wastedPropertyStorageBytes = 0; |
232 | long wastedPropertyStoragePercent = 0; |
233 | long objectWithOutOfLineStorageCount = 0; |
234 | long objectsWithOutOfLineStoragePercent = 0; |
235 | if ((storageStatistics.storageCapacity() > 0) && (storageStatistics.objectCount() > 0)) { |
236 | wastedPropertyStorageBytes = static_cast<long>((storageStatistics.storageCapacity() - storageStatistics.storageSize()) / KB); |
237 | wastedPropertyStoragePercent = static_cast<long>( |
238 | (storageStatistics.storageCapacity() - storageStatistics.storageSize()) * 100 / storageStatistics.storageCapacity()); |
239 | objectWithOutOfLineStorageCount = static_cast<long>(storageStatistics.objectWithOutOfLineStorageCount()); |
240 | objectsWithOutOfLineStoragePercent = objectWithOutOfLineStorageCount * 100 / storageStatistics.objectCount(); |
241 | } |
242 | dataLogF("wasted .property storage: %ldkB (%ld%%)\n" , wastedPropertyStorageBytes, wastedPropertyStoragePercent); |
243 | dataLogF("objects with out-of-line .property storage: %ld (%ld%%)\n" , objectWithOutOfLineStorageCount, objectsWithOutOfLineStoragePercent); |
244 | } |
245 | |
246 | } // namespace JSC |
247 | |