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() { ...@@ -9788,6 +9788,14 @@ v8::Platform* debug::GetCurrentPlatform() {
return i::V8::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) { debug::WasmScript* debug::WasmScript::Cast(debug::Script* script) {
CHECK(script->IsWasm()); CHECK(script->IsWasm());
return static_cast<WasmScript*>(script); return static_cast<WasmScript*>(script);
......
...@@ -521,6 +521,10 @@ bool SetFunctionBreakpoint(v8::Local<v8::Function> function, ...@@ -521,6 +521,10 @@ bool SetFunctionBreakpoint(v8::Local<v8::Function> function,
v8::Platform* GetCurrentPlatform(); v8::Platform* GetCurrentPlatform();
void ForceGarbageCollection(
v8::Isolate* isolate,
v8::EmbedderHeapTracer::EmbedderStackState embedder_stack_state);
class PostponeInterruptsScope { class PostponeInterruptsScope {
public: public:
explicit PostponeInterruptsScope(v8::Isolate* isolate); explicit PostponeInterruptsScope(v8::Isolate* isolate);
......
...@@ -25,7 +25,8 @@ ...@@ -25,7 +25,8 @@
"domain": "Profiler" "domain": "Profiler"
}, },
{ {
"domain": "HeapProfiler" "domain": "HeapProfiler",
"async": ["collectGarbage"]
} }
] ]
}, },
......
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
#include "src/inspector/v8-heap-profiler-agent-impl.h" #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/injected-script.h"
#include "src/inspector/inspected-context.h" #include "src/inspector/inspected-context.h"
#include "src/inspector/protocol/Protocol.h" #include "src/inspector/protocol/Protocol.h"
...@@ -12,10 +16,6 @@ ...@@ -12,10 +16,6 @@
#include "src/inspector/v8-inspector-impl.h" #include "src/inspector/v8-inspector-impl.h"
#include "src/inspector/v8-inspector-session-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 v8_inspector {
namespace { namespace {
...@@ -143,6 +143,36 @@ class HeapStatsStream final : public v8::OutputStream { ...@@ -143,6 +143,36 @@ class HeapStatsStream final : public v8::OutputStream {
} // namespace } // 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( V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(
V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel, V8InspectorSessionImpl* session, protocol::FrontendChannel* frontendChannel,
protocol::DictionaryValue* state) protocol::DictionaryValue* state)
...@@ -150,9 +180,14 @@ V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl( ...@@ -150,9 +180,14 @@ V8HeapProfilerAgentImpl::V8HeapProfilerAgentImpl(
m_isolate(session->inspector()->isolate()), m_isolate(session->inspector()->isolate()),
m_frontend(frontendChannel), m_frontend(frontendChannel),
m_state(state), 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() { void V8HeapProfilerAgentImpl::restore() {
if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled, if (m_state->booleanProperty(HeapProfilerAgentState::heapProfilerEnabled,
...@@ -171,9 +206,15 @@ void V8HeapProfilerAgentImpl::restore() { ...@@ -171,9 +206,15 @@ void V8HeapProfilerAgentImpl::restore() {
} }
} }
Response V8HeapProfilerAgentImpl::collectGarbage() { void V8HeapProfilerAgentImpl::collectGarbage(
m_isolate->LowMemoryNotification(); std::unique_ptr<CollectGarbageCallback> callback) {
return Response::Success(); 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( Response V8HeapProfilerAgentImpl::startTrackingHeapObjects(
......
...@@ -27,7 +27,8 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend { ...@@ -27,7 +27,8 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
~V8HeapProfilerAgentImpl() override; ~V8HeapProfilerAgentImpl() override;
void restore(); void restore();
Response collectGarbage() override; void collectGarbage(
std::unique_ptr<CollectGarbageCallback> callback) override;
Response enable() override; Response enable() override;
Response startTrackingHeapObjects(Maybe<bool> trackAllocations) override; Response startTrackingHeapObjects(Maybe<bool> trackAllocations) override;
...@@ -55,6 +56,9 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend { ...@@ -55,6 +56,9 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>*) override; std::unique_ptr<protocol::HeapProfiler::SamplingHeapProfile>*) override;
private: private:
struct AsyncGC;
class GCTask;
void startTrackingHeapObjectsInternal(bool trackAllocations); void startTrackingHeapObjectsInternal(bool trackAllocations);
void stopTrackingHeapObjectsInternal(); void stopTrackingHeapObjectsInternal();
void requestHeapStatsUpdate(); void requestHeapStatsUpdate();
...@@ -65,6 +69,7 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend { ...@@ -65,6 +69,7 @@ class V8HeapProfilerAgentImpl : public protocol::HeapProfiler::Backend {
protocol::HeapProfiler::Frontend m_frontend; protocol::HeapProfiler::Frontend m_frontend;
protocol::DictionaryValue* m_state; protocol::DictionaryValue* m_state;
bool m_hasTimer; bool m_hasTimer;
std::shared_ptr<AsyncGC> m_async_gc;
DISALLOW_COPY_AND_ASSIGN(V8HeapProfilerAgentImpl); DISALLOW_COPY_AND_ASSIGN(V8HeapProfilerAgentImpl);
}; };
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
// Flags: --allow-natives-syntax --no-always-opt --opt // Flags: --allow-natives-syntax --no-always-opt --opt
// Flags: --no-stress-flush-bytecode // Flags: --no-stress-flush-bytecode
// Flags: --no-stress-incremental-marking
var source = var source =
` `
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
// Flags: --allow-natives-syntax --no-always-opt --opt // Flags: --allow-natives-syntax --no-always-opt --opt
// Flags: --no-stress-flush-bytecode // Flags: --no-stress-flush-bytecode
// Flags: --no-stress-incremental-marking
var source = var source =
` `
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = InspectorTest.start( let {session, contextGroup, Protocol} = InspectorTest.start(
'Checks that inspector does not retain old collected scripts.\n'); 'Checks that inspector does not retain old collected scripts.\n');
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = let {session, contextGroup, Protocol} =
InspectorTest.start('Tests that we don\'t hold promises.'); InspectorTest.start('Tests that we don\'t hold promises.');
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = InspectorTest.start("Checks that inspector correctly process compiled scripts"); let {session, contextGroup, Protocol} = InspectorTest.start("Checks that inspector correctly process compiled scripts");
function addScripts() { 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 @@ ...@@ -100,14 +100,22 @@
############################################################################## ##############################################################################
['gc_stress or gc_fuzzer or variant == stress_incremental_marking', { ['gc_stress or gc_fuzzer or variant == stress_incremental_marking', {
# Skip tests that fail with GC stress: https://crbug.com/v8/10748 # 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': [SKIP],
'debugger/get-possible-breakpoints-array-literal': [SKIP], 'debugger/get-possible-breakpoints-array-literal': [SKIP],
'debugger/get-possible-breakpoints-master': [SKIP], 'debugger/get-possible-breakpoints-master': [SKIP],
'debugger/limit-size-of-collected-scripts': [SKIP],
'debugger/not-hold-promises': [SKIP],
'debugger/regression-424142': [SKIP], 'debugger/regression-424142': [SKIP],
'debugger/return-break-locations': [SKIP], 'debugger/return-break-locations': [SKIP],
'debugger/script-on-after-compile': [SKIP],
'debugger/set-breakpoint-at-last-line': [SKIP], 'debugger/set-breakpoint-at-last-line': [SKIP],
'debugger/set-breakpoint-breaks-on-first-breakable-location': [SKIP], 'debugger/set-breakpoint-breaks-on-first-breakable-location': [SKIP],
'heap-profiler/collect-garbage' : [SKIP],
'runtime-call-stats/collection': [SKIP], 'runtime-call-stats/collection': [SKIP],
'runtime/context-destroyed-on-context-collected': [SKIP],
'runtime/evaluate-async': [SKIP],
'runtime/internal-properties-entries': [SKIP], 'runtime/internal-properties-entries': [SKIP],
'type-profiler/type-profile-start-stop': [SKIP], 'type-profiler/type-profile-start-stop': [SKIP],
}], # gc_stress }], # gc_stress
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = let {session, contextGroup, Protocol} =
InspectorTest.start('Tests that contextDesrtoyed nofitication is fired when context is collected.'); InspectorTest.start('Tests that contextDesrtoyed nofitication is fired when context is collected.');
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flags: --no-stress-incremental-marking
let {session, contextGroup, Protocol} = InspectorTest.start("Tests that Runtime.evaluate works with awaitPromise flag."); let {session, contextGroup, Protocol} = InspectorTest.start("Tests that Runtime.evaluate works with awaitPromise flag.");
contextGroup.addScript(` 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