summaryrefslogtreecommitdiff
path: root/chromium/third_party/blink/renderer/controller/blink_leak_detector.cc
blob: 0d87117ff230f55edbc9cfee30bff41a8d855231 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/controller/blink_leak_detector.h"

#include "mojo/public/cpp/bindings/strong_binding.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h"
#include "third_party/blink/renderer/core/core_initializer.h"
#include "third_party/blink/renderer/core/css/css_default_style_sheets.h"
#include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_messaging_proxy.h"
#include "third_party/blink/renderer/core/workers/worker_thread.h"
#include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
#include "third_party/blink/renderer/platform/instance_counters.h"
#include "third_party/blink/renderer/platform/loader/fetch/memory_cache.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"

namespace blink {

BlinkLeakDetector::BlinkLeakDetector()
    : delayed_gc_timer_(Platform::Current()->CurrentThread()->GetTaskRunner(),
                        this,
                        &BlinkLeakDetector::TimerFiredGC) {}

BlinkLeakDetector::~BlinkLeakDetector() = default;

// static
void BlinkLeakDetector::Create(mojom::blink::LeakDetectorRequest request) {
  mojo::MakeStrongBinding(std::make_unique<BlinkLeakDetector>(),
                          std::move(request));
}

void BlinkLeakDetector::PerformLeakDetection(
    PerformLeakDetectionCallback callback) {
  callback_ = std::move(callback);

  v8::Isolate* isolate = v8::Isolate::GetCurrent();
  v8::HandleScope handle_scope(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::TerminateAllWorkersForTesting();
  GetMemoryCache()->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.
  // Currently PrepareForLeakDetection takes frame to get the spellchecker,
  // but in the future when leak detection runs with multiple frames,
  // this code must be refactored so that it iterates thru all the frames.
  for (Page* page : Page::OrdinaryPages()) {
    for (Frame* frame = page->MainFrame(); frame;
         frame = frame->Tree().TraverseNext()) {
      if (!frame->IsLocalFrame())
        continue;
      ToLocalFrame(frame)->GetSpellChecker().PrepareForLeakDetection();
    }
  }

  // FIXME: HTML5 Notification should be closed because notification affects
  // the result of number of DOM objects.
  V8PerIsolateData::From(isolate)->ClearScriptRegexpContext();

  // Clear lazily loaded style sheets.
  CSSDefaultStyleSheets::Instance().PrepareForLeakDetection();

  // Stop keepalive loaders that may persist after page navigation.
  for (auto resource_fetcher : ResourceFetcher::MainThreadFetchers())
    resource_fetcher->PrepareForLeakDetection();

  V8GCController::CollectAllGarbageForTesting(
      V8PerIsolateData::MainThreadIsolate());
  CoreInitializer::GetInstance().CollectAllGarbageForAnimationWorklet();
  // 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.
  number_of_gc_needed_ = 2;
  delayed_gc_timer_.StartOneShot(TimeDelta(), FROM_HERE);
}

void BlinkLeakDetector::TimerFiredGC(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.

  // Inspect counters on the next event loop.
  if (--number_of_gc_needed_ > 0) {
    delayed_gc_timer_.StartOneShot(TimeDelta(), FROM_HERE);
  } else if (number_of_gc_needed_ > -1 &&
             DedicatedWorkerMessagingProxy::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)
    delayed_gc_timer_.StartOneShot(TimeDelta(), FROM_HERE);
  } else {
    ReportResult();
  }

  V8GCController::CollectAllGarbageForTesting(
      V8PerIsolateData::MainThreadIsolate());
  CoreInitializer::GetInstance().CollectAllGarbageForAnimationWorklet();
  // Note: Oilpan precise GC is scheduled at the end of the event loop.
}

void BlinkLeakDetector::ReportResult() {
  mojom::blink::LeakDetectionResultPtr result =
      mojom::blink::LeakDetectionResult::New();
  result->number_of_live_audio_nodes =
      InstanceCounters::CounterValue(InstanceCounters::kAudioHandlerCounter);
  result->number_of_live_documents =
      InstanceCounters::CounterValue(InstanceCounters::kDocumentCounter);
  result->number_of_live_nodes =
      InstanceCounters::CounterValue(InstanceCounters::kNodeCounter);
  result->number_of_live_layout_objects =
      InstanceCounters::CounterValue(InstanceCounters::kLayoutObjectCounter);
  result->number_of_live_resources =
      InstanceCounters::CounterValue(InstanceCounters::kResourceCounter);
  result->number_of_live_pausable_objects =
      InstanceCounters::CounterValue(InstanceCounters::kPausableObjectCounter);
  result->number_of_live_script_promises =
      InstanceCounters::CounterValue(InstanceCounters::kScriptPromiseCounter);
  result->number_of_live_frames =
      InstanceCounters::CounterValue(InstanceCounters::kFrameCounter);
  result->number_of_live_v8_per_context_data = InstanceCounters::CounterValue(
      InstanceCounters::kV8PerContextDataCounter);
  result->number_of_worker_global_scopes = InstanceCounters::CounterValue(
      InstanceCounters::kWorkerGlobalScopeCounter);
  result->number_of_live_ua_css_resources =
      InstanceCounters::CounterValue(InstanceCounters::kUACSSResourceCounter);
  result->number_of_live_resource_fetchers =
      InstanceCounters::CounterValue(InstanceCounters::kResourceFetcherCounter);

#ifndef NDEBUG
  showLiveDocumentInstances();
#endif

  std::move(callback_).Run(std::move(result));
}

}  // namespace blink