Commit d1070e41 authored by Ulan Degenbaev's avatar Ulan Degenbaev Committed by Commit Bot

[inspector] Make collectGarbage of HeapProfiler precise

Instead of forcing GC right away, the function now post a task and
performance GC from the task with an empty stack to avoid false positive
pointers in conservative stack scanning.

Bug: chromium:1098187
Change-Id: I88864845a1e395056c5d5f6e867ad774b87dbb6a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2307217
Commit-Queue: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Reviewed-by: 's avatarPeter Marshall <petermarshall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69444}
parent 12b88d87
......@@ -9788,6 +9788,14 @@ v8::Platform* debug::GetCurrentPlatform() {
return i::V8::GetCurrentPlatform();
}
void debug::ForceGarbageCollection(
v8::Isolate* isolate,
v8::EmbedderHeapTracer::EmbedderStackState embedder_stack_state) {
i::Heap* heap = reinterpret_cast<i::Isolate*>(isolate)->heap();
heap->SetEmbedderStackStateForNextFinalizaton(embedder_stack_state);
isolate->LowMemoryNotification();
}
debug::WasmScript* debug::WasmScript::Cast(debug::Script* script) {
CHECK(script->IsWasm());
return static_cast<WasmScript*>(script);
......
......@@ -521,6 +521,10 @@ bool SetFunctionBreakpoint(v8::Local<v8::Function> function,
v8::Platform* GetCurrentPlatform();
void ForceGarbageCollection(
v8::Isolate* isolate,
v8::EmbedderHeapTracer::EmbedderStackState embedder_stack_state);
class PostponeInterruptsScope {
public:
explicit PostponeInterruptsScope(v8::Isolate* isolate);
......
......@@ -25,7 +25,8 @@
"domain": "Profiler"
},
{
"domain": "HeapProfiler"
"domain": "HeapProfiler",
"async": ["collectGarbage"]
}
]
},
......
......@@ -4,6 +4,10 @@
#include "src/inspector/v8-heap-profiler-agent-impl.h"
#include "include/v8-inspector.h"
#include "include/v8-profiler.h"
#include "include/v8-version.h"
#include "src/base/platform/mutex.h"
#include "src/inspector/injected-script.h"
#include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h"
......@@ -12,10 +16,6 @@
#include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-inspector-session-impl.h"
#include "include/v8-inspector.h"
#include "include/v8-profiler.h"
#include "include/v8-version.h"
namespace v8_inspector {
namespace {
......@@ -143,6 +143,36 @@ class HeapStatsStream final : public v8::OutputStream {
} // namespace
struct V8HeapProfilerAgentImpl::AsyncGC {
v8::base::Mutex m_mutex;
bool m_canceled = false;
bool m_pending = false;
std::vector<std::unique_ptr<CollectGarbageCallback>> m_pending_callbacks;
};
class V8HeapProfilerAgentImpl::GCTask : public v8::Task {
public:
GCTask(v8::Isolate* isolate, std::shared_ptr<AsyncGC> async_gc)
: m_isolate(isolate), m_async_gc(async_gc) {}
void Run() override {
std::shared_ptr<AsyncGC> async_gc = m_async_gc.lock();
if (!async_gc) return;
v8::base::MutexGuard lock(&async_gc->m_mutex);
if (async_gc->m_canceled) return;
v8::debug::ForceGarbageCollection(
m_isolate, v8::EmbedderHeapTracer::EmbedderStackState::kNoHeapPointers);
for (auto& callback : async_gc->m_pending_callbacks) {
callback->sendSuccess();
}
async_gc->m_pending_callbacks.clear();
}
private:
v8::Isolate* m_isolate;
std::weak_ptr<AsyncGC> m_async_gc;
};
V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
protocol::DictionaryValue* state)
......@@ -150,9 +180,14 @@ V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(
m_isolate(session->inspector()->isolate()),
m_frontend(frontendChannel),
m_state(state),
m_hasTimer(false) {}
m_hasTimer(false),
m_async_gc(std::make_shared<AsyncGC>()) {}
V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() = default;
V8HeapProfilerAgentImpl::~V8HeapProfilerAgentImpl() {
v8::base::MutexGuard lock(&m_async_gc->m_mutex);
m_async_gc->m_canceled = true;
m_async_gc->m_pending_callbacks.clear();
}
void V8HeapProfilerAgentImpl::restore() {
if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled,
......@@ -171,9 +206,15 @@ void V8HeapProfilerAgentImpl::restore() {
}
}
Response V8HeapProfilerAgentImpl::collectGarbage() {
m_isolate->LowMemoryNotification();
return Response::Success();
void V8HeapProfilerAgentImpl::collectGarbage(
std::unique_ptr<CollectGarbageCallback> callback) {
v8::base::MutexGuard lock(&m_async_gc->m_mutex);
m_async_gc->m_pending_callbacks.push_back(std::move(callback));
if (!m_async_gc->m_pending) {
v8::debug::GetCurrentPlatform()
->GetForegroundTaskRunner(m_isolate)
->PostNonNestableTask(std::make_unique<GCTask>(m_isolate, m_async_gc));
}
}
Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
......
......@@ -27,7 +27,8 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
~V8HeapProfilerAgentImpl() override;
void restore();
Response collectGarbage() override;
void collectGarbage(
std::unique_ptr<CollectGarbageCallback> callback) override;
Response enable() override;
Response startTrackingHeapObjects(Maybe<bool> trackAllocations) override;
......@@ -55,6 +56,9 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>*) override;
private:
struct AsyncGC;
class GCTask;
void startTrackingHeapObjectsInternal(bool trackAllocations);
void stopTrackingHeapObjectsInternal();
void requestHeapStatsUpdate();
......@@ -65,6 +69,7 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
protocol::HeapProfiler::Frontend m_frontend;
protocol::DictionaryValue* m_state;
bool m_hasTimer;
std::shared_ptr<AsyncGC> m_async_gc;
DISALLOW_COPY_AND_ASSIGN(V8HeapProfilerAgentImpl);
};
......
......@@ -4,6 +4,7 @@
// Flags: --allow-natives-syntax --no-always-opt --opt
// Flags: --no-stress-flush-bytecode
// Flags: --no-stress-incremental-marking
var source =
`
......
......@@ -4,6 +4,7 @@
// Flags: --allow-natives-syntax --no-always-opt --opt
// Flags: --no-stress-flush-bytecode
// Flags: --no-stress-incremental-marking
var source =
`
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = InspectorTest.start(
'Checks that inspector does not retain old collected scripts.\n');
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests that we don\'t hold promises.');
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = InspectorTest.start("Checks that inspector correctly process compiled scripts");
function addScripts() {
......
Tests collectGarbage.
Running test: testCollectGarbage
WeakRef state: WeakRef is cleared after GC.
\ No newline at end of file
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = InspectorTest.start(
'Tests collectGarbage.');
contextGroup.addScript(`
function createWeakRef() {
globalThis.weak_ref = new WeakRef(new Array(1000).fill(0));
}
function getWeakRef() {
if (!globalThis.weak_ref.deref()) return 'WeakRef is cleared after GC.';
return 'WeakRef is not cleared. GC did not happen?'
}
//# sourceURL=test.js`);
Protocol.Debugger.enable();
Protocol.HeapProfiler.enable();
InspectorTest.runAsyncTestSuite([
async function testCollectGarbage() {
await Protocol.Runtime.evaluate({ expression: 'createWeakRef()' });
await Protocol.HeapProfiler.collectGarbage();
let weak_ref = await Protocol.Runtime.evaluate({ expression: 'getWeakRef()' });
InspectorTest.log(`WeakRef state: ${weak_ref.result.result.value}`);
}
]);
......@@ -100,14 +100,22 @@
##############################################################################
['gc_stress or gc_fuzzer or variant == stress_incremental_marking', {
# Skip tests that fail with GC stress: https://crbug.com/v8/10748
'cpu-profiler/coverage': [SKIP],
'cpu-profiler/coverage-block': [SKIP],
'debugger/get-possible-breakpoints': [SKIP],
'debugger/get-possible-breakpoints-array-literal': [SKIP],
'debugger/get-possible-breakpoints-master': [SKIP],
'debugger/limit-size-of-collected-scripts': [SKIP],
'debugger/not-hold-promises': [SKIP],
'debugger/regression-424142': [SKIP],
'debugger/return-break-locations': [SKIP],
'debugger/script-on-after-compile': [SKIP],
'debugger/set-breakpoint-at-last-line': [SKIP],
'debugger/set-breakpoint-breaks-on-first-breakable-location': [SKIP],
'heap-profiler/collect-garbage' : [SKIP],
'runtime-call-stats/collection': [SKIP],
'runtime/context-destroyed-on-context-collected': [SKIP],
'runtime/evaluate-async': [SKIP],
'runtime/internal-properties-entries': [SKIP],
'type-profiler/type-profile-start-stop': [SKIP],
}], # gc_stress
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} =
InspectorTest.start('Tests that contextDesrtoyed nofitication is fired when context is collected.');
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = InspectorTest.start("Tests that Runtime.evaluate works with awaitPromise flag.");
contextGroup.addScript(`
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment