1/*
2 * Copyright (C) 2007-2017 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "GCController.h"
28
29#include "CommonVM.h"
30#include "JSHTMLDocument.h"
31#include "Location.h"
32#include <JavaScriptCore/Heap.h>
33#include <JavaScriptCore/HeapSnapshotBuilder.h>
34#include <JavaScriptCore/JSLock.h>
35#include <JavaScriptCore/VM.h>
36#include <pal/Logging.h>
37#include <wtf/FastMalloc.h>
38#include <wtf/FileSystem.h>
39#include <wtf/NeverDestroyed.h>
40#include <wtf/StdLibExtras.h>
41
42namespace WebCore {
43using namespace JSC;
44
45static void collect()
46{
47 JSLockHolder lock(commonVM());
48 commonVM().heap.collectNow(Async, CollectionScope::Full);
49}
50
51GCController& GCController::singleton()
52{
53 static NeverDestroyed<GCController> controller;
54 return controller;
55}
56
57GCController::GCController()
58 : m_GCTimer(*this, &GCController::gcTimerFired)
59{
60 static std::once_flag onceFlag;
61 std::call_once(onceFlag, [] {
62 PAL::registerNotifyCallback("com.apple.WebKit.dumpGCHeap", [] {
63 GCController::singleton().dumpHeap();
64 });
65 });
66}
67
68void GCController::garbageCollectSoon()
69{
70 // We only use reportAbandonedObjectGraph for systems for which there's an implementation
71 // of the garbage collector timers in JavaScriptCore. We wouldn't need this if JavaScriptCore
72 // used a timer implementation from WTF like RunLoop::Timer.
73#if USE(CF) || USE(GLIB)
74 JSLockHolder lock(commonVM());
75 commonVM().heap.reportAbandonedObjectGraph();
76#else
77 garbageCollectOnNextRunLoop();
78#endif
79}
80
81void GCController::garbageCollectOnNextRunLoop()
82{
83 if (!m_GCTimer.isActive())
84 m_GCTimer.startOneShot(0_s);
85}
86
87void GCController::gcTimerFired()
88{
89 collect();
90}
91
92void GCController::garbageCollectNow()
93{
94 JSLockHolder lock(commonVM());
95 if (!commonVM().heap.isCurrentThreadBusy()) {
96 commonVM().heap.collectNow(Sync, CollectionScope::Full);
97 WTF::releaseFastMallocFreeMemory();
98 }
99}
100
101void GCController::garbageCollectNowIfNotDoneRecently()
102{
103#if USE(CF) || USE(GLIB)
104 JSLockHolder lock(commonVM());
105 if (!commonVM().heap.isCurrentThreadBusy())
106 commonVM().heap.collectNowFullIfNotDoneRecently(Async);
107#else
108 garbageCollectSoon();
109#endif
110}
111
112void GCController::garbageCollectOnAlternateThreadForDebugging(bool waitUntilDone)
113{
114 auto thread = Thread::create("WebCore: GCController", &collect);
115
116 if (waitUntilDone) {
117 thread->waitForCompletion();
118 return;
119 }
120
121 thread->detach();
122}
123
124void GCController::setJavaScriptGarbageCollectorTimerEnabled(bool enable)
125{
126 commonVM().heap.setGarbageCollectionTimerEnabled(enable);
127}
128
129void GCController::deleteAllCode(DeleteAllCodeEffort effort)
130{
131 JSLockHolder lock(commonVM());
132 commonVM().deleteAllCode(effort);
133}
134
135void GCController::deleteAllLinkedCode(DeleteAllCodeEffort effort)
136{
137 JSLockHolder lock(commonVM());
138 commonVM().deleteAllLinkedCode(effort);
139}
140
141void GCController::dumpHeap()
142{
143 FileSystem::PlatformFileHandle fileHandle;
144 String tempFilePath = FileSystem::openTemporaryFile("GCHeap"_s, fileHandle);
145 if (!FileSystem::isHandleValid(fileHandle)) {
146 WTFLogAlways("Dumping GC heap failed to open temporary file");
147 return;
148 }
149
150 VM& vm = commonVM();
151 JSLockHolder lock(vm);
152
153 sanitizeStackForVM(&vm);
154
155 String jsonData;
156 {
157 DeferGCForAWhile deferGC(vm.heap); // Prevent concurrent GC from interfering with the full GC that the snapshot does.
158
159 HeapSnapshotBuilder snapshotBuilder(vm.ensureHeapProfiler(), HeapSnapshotBuilder::SnapshotType::GCDebuggingSnapshot);
160 snapshotBuilder.buildSnapshot();
161
162 jsonData = snapshotBuilder.json();
163 }
164
165 CString utf8String = jsonData.utf8();
166
167 FileSystem::writeToFile(fileHandle, utf8String.data(), utf8String.length());
168 FileSystem::closeFile(fileHandle);
169
170 WTFLogAlways("Dumped GC heap to %s", tempFilePath.utf8().data());
171}
172
173} // namespace WebCore
174