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 "JSObject.h"
31#include "Operations.h"
32#include "Options.h"
33#include <stdlib.h>
34#if OS(UNIX)
35#include <sys/resource.h>
36#endif
37#include <wtf/CurrentTime.h>
38#include <wtf/DataLog.h>
39#include <wtf/Deque.h>
40
41namespace JSC {
42
43double HeapStatistics::s_startTime = 0.0;
44double HeapStatistics::s_endTime = 0.0;
45Vector<double>* HeapStatistics::s_pauseTimeStarts = 0;
46Vector<double>* HeapStatistics::s_pauseTimeEnds = 0;
47
48#if OS(UNIX)
49
50void HeapStatistics::initialize()
51{
52    ASSERT(Options::recordGCPauseTimes());
53    s_startTime = WTF::monotonicallyIncreasingTime();
54    s_pauseTimeStarts = new Vector<double>();
55    s_pauseTimeEnds = new Vector<double>();
56}
57
58void HeapStatistics::recordGCPauseTime(double start, double end)
59{
60    ASSERT(Options::recordGCPauseTimes());
61    ASSERT(s_pauseTimeStarts);
62    ASSERT(s_pauseTimeEnds);
63    s_pauseTimeStarts->append(start);
64    s_pauseTimeEnds->append(end);
65}
66
67void HeapStatistics::logStatistics()
68{
69    struct rusage usage;
70    getrusage(RUSAGE_SELF, &usage);
71#if USE(CF) || OS(UNIX)
72    char* vmName = getenv("JSVMName");
73    char* suiteName = getenv("JSSuiteName");
74    char* benchmarkName = getenv("JSBenchmarkName");
75#else
76#error "The HeapStatistics module is not supported on this platform."
77#endif
78    if (!vmName || !suiteName || !benchmarkName)
79        dataLogF("HeapStatistics: {\"max_rss\": %ld", usage.ru_maxrss);
80    else
81        dataLogF("HeapStatistics: {\"max_rss\": %ld, \"vm_name\": \"%s\", \"suite_name\": \"%s\", \"benchmark_name\": \"%s\"",
82            usage.ru_maxrss, vmName, suiteName, benchmarkName);
83
84    if (Options::recordGCPauseTimes()) {
85        dataLogF(", \"pause_times\": [");
86        Vector<double>::iterator startIt = s_pauseTimeStarts->begin();
87        Vector<double>::iterator endIt = s_pauseTimeEnds->begin();
88        if (startIt != s_pauseTimeStarts->end() && endIt != s_pauseTimeEnds->end()) {
89            dataLogF("[%f, %f]", *startIt, *endIt);
90            ++startIt;
91            ++endIt;
92        }
93        while (startIt != s_pauseTimeStarts->end() && endIt != s_pauseTimeEnds->end()) {
94            dataLogF(", [%f, %f]", *startIt, *endIt);
95            ++startIt;
96            ++endIt;
97        }
98        dataLogF("], \"start_time\": %f, \"end_time\": %f", s_startTime, s_endTime);
99    }
100    dataLogF("}\n");
101}
102
103void HeapStatistics::exitWithFailure()
104{
105    ASSERT(Options::logHeapStatisticsAtExit());
106    s_endTime = WTF::monotonicallyIncreasingTime();
107    logStatistics();
108    exit(-1);
109}
110
111void HeapStatistics::reportSuccess()
112{
113    ASSERT(Options::logHeapStatisticsAtExit());
114    s_endTime = WTF::monotonicallyIncreasingTime();
115    logStatistics();
116}
117
118#else
119
120void HeapStatistics::initialize()
121{
122}
123
124void HeapStatistics::recordGCPauseTime(double, double)
125{
126}
127
128void HeapStatistics::logStatistics()
129{
130}
131
132void HeapStatistics::exitWithFailure()
133{
134}
135
136void HeapStatistics::reportSuccess()
137{
138}
139
140#endif // OS(UNIX)
141
142size_t HeapStatistics::parseMemoryAmount(char* s)
143{
144    size_t multiplier = 1;
145    char* afterS;
146    size_t value = strtol(s, &afterS, 10);
147    char next = afterS[0];
148    switch (next) {
149    case 'K':
150        multiplier = KB;
151        break;
152    case 'M':
153        multiplier = MB;
154        break;
155    case 'G':
156        multiplier = GB;
157        break;
158    default:
159        break;
160    }
161    return value * multiplier;
162}
163
164class StorageStatistics : public MarkedBlock::VoidFunctor {
165public:
166    StorageStatistics();
167
168    void operator()(JSCell*);
169
170    size_t objectWithOutOfLineStorageCount();
171    size_t objectCount();
172
173    size_t storageSize();
174    size_t storageCapacity();
175
176private:
177    size_t m_objectWithOutOfLineStorageCount;
178    size_t m_objectCount;
179    size_t m_storageSize;
180    size_t m_storageCapacity;
181};
182
183inline StorageStatistics::StorageStatistics()
184    : m_objectWithOutOfLineStorageCount(0)
185    , m_objectCount(0)
186    , m_storageSize(0)
187    , m_storageCapacity(0)
188{
189}
190
191inline void StorageStatistics::operator()(JSCell* cell)
192{
193    if (!cell->isObject())
194        return;
195
196    JSObject* object = jsCast<JSObject*>(cell);
197    if (hasIndexedProperties(object->structure()->indexingType()))
198        return;
199
200    if (object->structure()->isUncacheableDictionary())
201        return;
202
203    ++m_objectCount;
204    if (!object->hasInlineStorage())
205        ++m_objectWithOutOfLineStorageCount;
206    m_storageSize += object->structure()->totalStorageSize() * sizeof(WriteBarrierBase<Unknown>);
207    m_storageCapacity += object->structure()->totalStorageCapacity() * sizeof(WriteBarrierBase<Unknown>);
208}
209
210inline size_t StorageStatistics::objectWithOutOfLineStorageCount()
211{
212    return m_objectWithOutOfLineStorageCount;
213}
214
215inline size_t StorageStatistics::objectCount()
216{
217    return m_objectCount;
218}
219
220inline size_t StorageStatistics::storageSize()
221{
222    return m_storageSize;
223}
224
225inline size_t StorageStatistics::storageCapacity()
226{
227    return m_storageCapacity;
228}
229
230void HeapStatistics::showObjectStatistics(Heap* heap)
231{
232    dataLogF("\n=== Heap Statistics: ===\n");
233    dataLogF("size: %ldkB\n", static_cast<long>(heap->m_sizeAfterLastCollect / KB));
234    dataLogF("capacity: %ldkB\n", static_cast<long>(heap->capacity() / KB));
235    dataLogF("pause time: %lfms\n\n", heap->m_lastGCLength);
236
237    StorageStatistics storageStatistics;
238    heap->m_objectSpace.forEachLiveCell(storageStatistics);
239    dataLogF("wasted .property storage: %ldkB (%ld%%)\n",
240        static_cast<long>(
241            (storageStatistics.storageCapacity() - storageStatistics.storageSize()) / KB),
242        static_cast<long>(
243            (storageStatistics.storageCapacity() - storageStatistics.storageSize()) * 100
244                / storageStatistics.storageCapacity()));
245    dataLogF("objects with out-of-line .property storage: %ld (%ld%%)\n",
246        static_cast<long>(
247            storageStatistics.objectWithOutOfLineStorageCount()),
248        static_cast<long>(
249            storageStatistics.objectWithOutOfLineStorageCount() * 100
250                / storageStatistics.objectCount()));
251}
252
253} // namespace JSC
254