blob: 0b648c3b675ef05947f8794803aada0ec1fa5acc [file] [log] [blame]
/*
* Copyright (C) 2014 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "public/web/WebLeakDetector.h"
#include "bindings/core/v8/V8GCController.h"
#include "core/editing/spellcheck/SpellChecker.h"
#include "core/fetch/MemoryCache.h"
#include "core/inspector/InstanceCounters.h"
#include "core/workers/InProcessWorkerMessagingProxy.h"
#include "core/workers/WorkerThread.h"
#include "modules/compositorworker/AbstractAnimationWorkletThread.h"
#include "platform/Timer.h"
#include "public/web/WebFrame.h"
#include "web/WebLocalFrameImpl.h"
namespace blink {
namespace {
class WebLeakDetectorImpl final : public WebLeakDetector {
WTF_MAKE_NONCOPYABLE(WebLeakDetectorImpl);
public:
explicit WebLeakDetectorImpl(WebLeakDetectorClient* client)
: m_client(client),
m_delayedGCAndReportTimer(this,
&WebLeakDetectorImpl::delayedGCAndReport),
m_delayedReportTimer(this, &WebLeakDetectorImpl::delayedReport),
m_numberOfGCNeeded(0) {
DCHECK(m_client);
}
~WebLeakDetectorImpl() override {}
void prepareForLeakDetection(WebFrame*) override;
void collectGarbageAndReport() override;
private:
void delayedGCAndReport(TimerBase*);
void delayedReport(TimerBase*);
WebLeakDetectorClient* m_client;
Timer<WebLeakDetectorImpl> m_delayedGCAndReportTimer;
Timer<WebLeakDetectorImpl> m_delayedReportTimer;
int m_numberOfGCNeeded;
};
void WebLeakDetectorImpl::prepareForLeakDetection(WebFrame* frame) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
// For example, calling isValidEmailAddress in EmailInputType.cpp with a
// non-empty string creates a static ScriptRegexp value which holds a
// V8PerContextData indirectly. This affects the number of V8PerContextData.
// To ensure that context data is created, call ensureScriptRegexpContext
// here.
V8PerIsolateData::from(isolate)->ensureScriptRegexpContext();
WorkerThread::terminateAndWaitForAllWorkers();
memoryCache()->evictResources();
// If the spellchecker is allowed to continue issuing requests while the
// leak detector runs, leaks may flakily be reported as the requests keep
// their associated element (and document) alive.
//
// Stop the spellchecker to prevent this.
if (frame->isWebLocalFrame()) {
WebLocalFrameImpl* localFrame = toWebLocalFrameImpl(frame);
SpellChecker& spellChecker = localFrame->frame()->spellChecker();
spellChecker.prepareForLeakDetection();
}
// FIXME: HTML5 Notification should be closed because notification affects the
// result of number of DOM objects.
V8PerIsolateData::from(isolate)->clearScriptRegexpContext();
}
void WebLeakDetectorImpl::collectGarbageAndReport() {
V8GCController::collectAllGarbageForTesting(
V8PerIsolateData::mainThreadIsolate());
AbstractAnimationWorkletThread::collectAllGarbage();
// Note: Oilpan precise GC is scheduled at the end of the event loop.
// Task queue may contain delayed object destruction tasks.
// This method is called from navigation hook inside FrameLoader,
// so previous document is still held by the loader until the next event loop.
// Complete all pending tasks before proceeding to gc.
m_numberOfGCNeeded = 2;
m_delayedGCAndReportTimer.startOneShot(0, BLINK_FROM_HERE);
}
void WebLeakDetectorImpl::delayedGCAndReport(TimerBase*) {
// We do a second and third GC here to address flakiness
// The second GC is necessary as Resource GC may have postponed clean-up tasks
// to next event loop. The third GC is necessary for cleaning up Document
// after worker object died.
V8GCController::collectAllGarbageForTesting(
V8PerIsolateData::mainThreadIsolate());
AbstractAnimationWorkletThread::collectAllGarbage();
// Note: Oilpan precise GC is scheduled at the end of the event loop.
// Inspect counters on the next event loop.
if (--m_numberOfGCNeeded > 0) {
m_delayedGCAndReportTimer.startOneShot(0, BLINK_FROM_HERE);
} else if (m_numberOfGCNeeded > -1 &&
InProcessWorkerMessagingProxy::proxyCount()) {
// It is possible that all posted tasks for finalizing in-process proxy
// objects will not have run before the final round of GCs started. If so,
// do yet another pass, letting these tasks run and then afterwards perform
// a GC to tidy up.
//
// TODO(sof): use proxyCount() to always decide if another GC needs to be
// scheduled. Some debug bots running browser unit tests disagree
// (crbug.com/616714)
m_delayedGCAndReportTimer.startOneShot(0, BLINK_FROM_HERE);
} else {
m_delayedReportTimer.startOneShot(0, BLINK_FROM_HERE);
}
}
void WebLeakDetectorImpl::delayedReport(TimerBase*) {
DCHECK(m_client);
WebLeakDetectorClient::Result result;
result.numberOfLiveAudioNodes =
InstanceCounters::counterValue(InstanceCounters::AudioHandlerCounter);
result.numberOfLiveDocuments =
InstanceCounters::counterValue(InstanceCounters::DocumentCounter);
result.numberOfLiveNodes =
InstanceCounters::counterValue(InstanceCounters::NodeCounter);
result.numberOfLiveLayoutObjects =
InstanceCounters::counterValue(InstanceCounters::LayoutObjectCounter);
result.numberOfLiveResources =
InstanceCounters::counterValue(InstanceCounters::ResourceCounter);
result.numberOfLiveActiveDOMObjects =
InstanceCounters::counterValue(InstanceCounters::ActiveDOMObjectCounter);
result.numberOfLiveScriptPromises =
InstanceCounters::counterValue(InstanceCounters::ScriptPromiseCounter);
result.numberOfLiveFrames =
InstanceCounters::counterValue(InstanceCounters::FrameCounter);
result.numberOfLiveV8PerContextData =
InstanceCounters::counterValue(InstanceCounters::V8PerContextDataCounter);
result.numberOfWorkerGlobalScopes = InstanceCounters::counterValue(
InstanceCounters::WorkerGlobalScopeCounter);
m_client->onLeakDetectionComplete(result);
#ifndef NDEBUG
showLiveDocumentInstances();
#endif
}
} // namespace
WebLeakDetector* WebLeakDetector::create(WebLeakDetectorClient* client) {
return new WebLeakDetectorImpl(client);
}
} // namespace blink