[heap] Add StackStartMarker for cppgc
For conservative stack scanning we currently scan for references up until the stack top. But this includes stack frames like that of _start which definitely do not have pointers into the cppgc heap. This CL adds a StackStartMarker class which embedders can optionally use to decrease the stack height considered during conservative stack scanning. This reduces the chance of false positives. Bug: 492198560 Change-Id: Ic9a7d428684bb835d372873beb821e412e131246 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/7666257 Commit-Queue: Dominik Inführ <dinfuehr@chromium.org> Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Cr-Commit-Position: refs/heads/main@{#105816}
This commit is contained in:
1 parent
17898c8b37
commit
b3cb54c7e6
13 files changed
+151
-16
No files matched your search
+18
-1
@@ -32,6 +32,18 @@ namespace internal {
|
||||
class Heap;
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* A marker that captures the current stack start address.
|
||||
*/
|
||||
class V8_EXPORT StackStartMarker {
|
||||
public:
|
||||
StackStartMarker() : stack_start_(__builtin_frame_address(0)) {}
|
||||
void* stack_start() const { return stack_start_; }
|
||||
|
||||
private:
|
||||
void* stack_start_;
|
||||
};
|
||||
|
||||
class V8_EXPORT Heap {
|
||||
public:
|
||||
/**
|
||||
@@ -151,8 +163,13 @@ class V8_EXPORT Heap {
|
||||
* GC scheduler follows.
|
||||
*/
|
||||
ResourceConstraints resource_constraints;
|
||||
};
|
||||
|
||||
/**
|
||||
* Optional marker representing the stack start of the thread creating the
|
||||
* heap.
|
||||
*/
|
||||
std::optional<StackStartMarker> stack_start_marker = std::nullopt;
|
||||
};
|
||||
/**
|
||||
* Creates a new heap that can be used for object allocation.
|
||||
*
|
||||
|
||||
@@ -53,6 +53,11 @@ struct V8_EXPORT CppHeapCreateParams {
|
||||
*/
|
||||
cppgc::Heap::SweepingType sweeping_support =
|
||||
cppgc::Heap::SweepingType::kIncrementalAndConcurrent;
|
||||
/**
|
||||
* Optional marker representing the stack start of the thread creating the
|
||||
* heap.
|
||||
*/
|
||||
std::optional<cppgc::StackStartMarker> stack_start_marker = std::nullopt;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,9 +34,9 @@ using StackVisitorCallback = void (*)(StackVisitor*);
|
||||
// of relevant GC stack regions where interesting pointers can be found.
|
||||
class V8_EXPORT_PRIVATE Stack final {
|
||||
public:
|
||||
// Sets the start of the stack to the current stack start.
|
||||
void SetStackStart() {
|
||||
current_segment_.start = v8::base::Stack::GetStackStart();
|
||||
// Sets the start of the stack to the provided stack start.
|
||||
void SetStackStart(void* stack_start) {
|
||||
current_segment_.start = stack_start;
|
||||
#ifdef V8_USE_ADDRESS_SANITIZER
|
||||
current_segment_.asan_fake_stack = __asan_get_current_fake_stack();
|
||||
#endif // V8_USE_ADDRESS_SANITIZER
|
||||
@@ -45,6 +45,8 @@ class V8_EXPORT_PRIVATE Stack final {
|
||||
#endif // V8_USE_SAFE_STACK
|
||||
}
|
||||
|
||||
const void* stack_start() const { return current_segment_.start; }
|
||||
|
||||
// Returns true if |slot| is part of the stack and false otherwise.
|
||||
// It is a static method, ignoring the set stack start and marker, but it
|
||||
// considers the ASAN stack and SafeStack.
|
||||
|
||||
@@ -103,9 +103,9 @@ class MinorGCHeapGrowing
|
||||
// static
|
||||
std::unique_ptr<CppHeap> CppHeap::Create(v8::Platform* platform,
|
||||
const CppHeapCreateParams& params) {
|
||||
return std::make_unique<internal::CppHeap>(platform, params.custom_spaces,
|
||||
params.marking_support,
|
||||
params.sweeping_support);
|
||||
return std::make_unique<internal::CppHeap>(
|
||||
platform, params.custom_spaces, params.marking_support,
|
||||
params.sweeping_support, params.stack_start_marker);
|
||||
}
|
||||
|
||||
cppgc::AllocationHandle& CppHeap::GetAllocationHandle() {
|
||||
@@ -517,12 +517,13 @@ CppHeap::CppHeap(
|
||||
v8::Platform* platform,
|
||||
const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>& custom_spaces,
|
||||
cppgc::Heap::MarkingType marking_support,
|
||||
cppgc::Heap::SweepingType sweeping_support)
|
||||
cppgc::Heap::SweepingType sweeping_support,
|
||||
std::optional<cppgc::StackStartMarker> stack_start_marker)
|
||||
: cppgc::internal::HeapBase(
|
||||
std::make_shared<CppgcPlatformAdapter>(platform), custom_spaces,
|
||||
cppgc::internal::HeapBase::StackSupport::
|
||||
kSupportsConservativeStackScan,
|
||||
marking_support, sweeping_support, *this),
|
||||
marking_support, sweeping_support, *this, stack_start_marker),
|
||||
minor_gc_heap_growing_(
|
||||
std::make_unique<MinorGCHeapGrowing>(*stats_collector())),
|
||||
cross_heap_remembered_set_(*this) {
|
||||
|
||||
@@ -110,7 +110,8 @@ class V8_EXPORT_PRIVATE CppHeap final
|
||||
|
||||
CppHeap(v8::Platform*,
|
||||
const std::vector<std::unique_ptr<cppgc::CustomSpaceBase>>&,
|
||||
cppgc::Heap::MarkingType, cppgc::Heap::SweepingType);
|
||||
cppgc::Heap::MarkingType, cppgc::Heap::SweepingType,
|
||||
std::optional<cppgc::StackStartMarker>);
|
||||
~CppHeap() final;
|
||||
|
||||
CppHeap(const CppHeap&) = delete;
|
||||
|
||||
@@ -100,7 +100,8 @@ HeapBase::HeapBase(
|
||||
std::shared_ptr<cppgc::Platform> platform,
|
||||
const std::vector<std::unique_ptr<CustomSpaceBase>>& custom_spaces,
|
||||
StackSupport stack_support, MarkingType marking_support,
|
||||
SweepingType sweeping_support, GarbageCollector& garbage_collector)
|
||||
SweepingType sweeping_support, GarbageCollector& garbage_collector,
|
||||
std::optional<cppgc::StackStartMarker> stack_start_marker)
|
||||
: raw_heap_(this, custom_spaces),
|
||||
platform_(std::move(platform)),
|
||||
oom_handler_(std::make_unique<FatalOutOfMemoryHandler>(this)),
|
||||
@@ -129,7 +130,11 @@ HeapBase::HeapBase(
|
||||
sweeping_support_(sweeping_support) {
|
||||
stats_collector_->RegisterObserver(
|
||||
&allocation_observer_for_PROCESS_HEAP_STATISTICS_);
|
||||
stack_->SetStackStart();
|
||||
if (stack_start_marker) {
|
||||
stack_->SetStackStart(stack_start_marker->stack_start());
|
||||
} else {
|
||||
stack_->SetStackStart(v8::base::Stack::GetStackStart());
|
||||
}
|
||||
}
|
||||
|
||||
HeapBase::~HeapBase() = default;
|
||||
|
||||
@@ -95,7 +95,8 @@ class V8_EXPORT_PRIVATE HeapBase : public cppgc::HeapHandle {
|
||||
HeapBase(std::shared_ptr<cppgc::Platform> platform,
|
||||
const std::vector<std::unique_ptr<CustomSpaceBase>>& custom_spaces,
|
||||
StackSupport stack_support, MarkingType marking_support,
|
||||
SweepingType sweeping_support, GarbageCollector& garbage_collector);
|
||||
SweepingType sweeping_support, GarbageCollector& garbage_collector,
|
||||
std::optional<cppgc::StackStartMarker> stack_start_marker);
|
||||
virtual ~HeapBase();
|
||||
|
||||
HeapBase(const HeapBase&) = delete;
|
||||
|
||||
@@ -75,7 +75,8 @@ void CheckConfig(GCConfig config, HeapBase::MarkingType marking_support,
|
||||
Heap::Heap(std::shared_ptr<cppgc::Platform> platform,
|
||||
cppgc::Heap::HeapOptions options)
|
||||
: HeapBase(platform, options.custom_spaces, options.stack_support,
|
||||
options.marking_support, options.sweeping_support, gc_invoker_),
|
||||
options.marking_support, options.sweeping_support, gc_invoker_,
|
||||
options.stack_start_marker),
|
||||
gc_invoker_(this, platform_.get(), options.stack_support),
|
||||
growing_(&gc_invoker_, stats_collector_.get(),
|
||||
options.resource_constraints, options.marking_support,
|
||||
|
||||
+2
-1
@@ -25,6 +25,7 @@
|
||||
#include "src/base/once.h"
|
||||
#include "src/base/platform/memory.h"
|
||||
#include "src/base/platform/mutex.h"
|
||||
#include "src/base/platform/platform.h"
|
||||
#include "src/base/platform/time.h"
|
||||
#include "src/base/utils/random-number-generator.h"
|
||||
#include "src/builtins/accessors.h"
|
||||
@@ -6395,7 +6396,7 @@ void Heap::SetStackStart() {
|
||||
// If no main thread local heap has been set up (we're still in the
|
||||
// deserialization process), we don't need to set the stack start.
|
||||
if (main_thread_local_heap_ == nullptr) return;
|
||||
stack().SetStackStart();
|
||||
stack().SetStackStart(v8::base::Stack::GetStackStart());
|
||||
}
|
||||
|
||||
::heap::base::Stack& Heap::stack() {
|
||||
|
||||
@@ -155,6 +155,7 @@ v8_cluster_source_set("cppgc_unittests_sources") {
|
||||
"heap/cppgc/prefinalizer-unittest.cc",
|
||||
"heap/cppgc/sanitizer-unittest.cc",
|
||||
"heap/cppgc/source-location-unittest.cc",
|
||||
"heap/cppgc/stack-start-marker-unittest.cc",
|
||||
"heap/cppgc/stack-unittest.cc",
|
||||
"heap/cppgc/stats-collector-scopes-unittest.cc",
|
||||
"heap/cppgc/stats-collector-unittest.cc",
|
||||
@@ -370,6 +371,7 @@ v8_cluster_source_set("v8_unittests_sources") {
|
||||
"heap/code-range-unittest.cc",
|
||||
"heap/collection-barrier-unittest.cc",
|
||||
"heap/conservative-stack-visitor-unittest.cc",
|
||||
"heap/cppgc-js/cpp-heap-stack-start-marker-unittest.cc",
|
||||
"heap/cppgc-js/embedder-roots-handler-unittest.cc",
|
||||
"heap/cppgc-js/traced-reference-unittest.cc",
|
||||
"heap/cppgc-js/unified-heap-snapshot-unittest.cc",
|
||||
@@ -543,6 +545,7 @@ v8_cluster_source_set("v8_unittests_sources") {
|
||||
"compiler/run-variables-unittest.cc",
|
||||
"compiler/linear-scheduler-unittest.cc",
|
||||
"compiler/linkage-unittest.cc",
|
||||
"heap/cppgc-js/cpp-heap-stack-start-marker-unittest.cc",
|
||||
"heap/cppgc-js/embedder-roots-handler-unittest.cc",
|
||||
"heap/cppgc-js/unified-heap-snapshot-unittest.cc",
|
||||
"heap/cppgc-js/unified-heap-unittest.cc",
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/libplatform/libplatform.h"
|
||||
#include "include/v8-cppgc.h"
|
||||
#include "src/heap/cppgc-js/cpp-heap.h"
|
||||
#include "test/unittests/test-utils.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
class CppHeapStackStartMarkerTest : public TestWithPlatform {};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(CppHeapStackStartMarkerTest, StackStartMarkerIsUsed) {
|
||||
cppgc::StackStartMarker marker;
|
||||
CppHeapCreateParams params({});
|
||||
params.stack_start_marker = marker;
|
||||
|
||||
auto cpp_heap = CppHeap::Create(platform(), params);
|
||||
CppHeap* internal_heap = CppHeap::From(cpp_heap.get());
|
||||
|
||||
EXPECT_EQ(marker.stack_start(), internal_heap->stack()->stack_start());
|
||||
EXPECT_LT(internal_heap->stack()->stack_start(),
|
||||
v8::base::Stack::GetStackStart());
|
||||
}
|
||||
|
||||
TEST_F(CppHeapStackStartMarkerTest, DefaultWithoutStackStartMarker) {
|
||||
CppHeapCreateParams params({});
|
||||
EXPECT_FALSE(params.stack_start_marker.has_value());
|
||||
|
||||
auto cpp_heap = CppHeap::Create(platform(), params);
|
||||
CppHeap* internal_heap = CppHeap::From(cpp_heap.get());
|
||||
|
||||
// Without a marker, it should use the default stack start.
|
||||
EXPECT_EQ(v8::base::Stack::GetStackStart(),
|
||||
internal_heap->stack()->stack_start());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2026 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.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/cppgc/heap.h"
|
||||
#include "src/heap/base/stack.h"
|
||||
#include "src/heap/cppgc/heap.h"
|
||||
#include "test/unittests/heap/cppgc/tests.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace cppgc {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
class StackStartMarkerTest : public testing::TestWithPlatform {};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST_F(StackStartMarkerTest, StackStartMarkerIsUsed) {
|
||||
StackStartMarker marker;
|
||||
Heap::HeapOptions options;
|
||||
options.stack_start_marker = marker;
|
||||
|
||||
auto heap = Heap::Create(GetPlatformHandle(), std::move(options));
|
||||
Heap* internal_heap = Heap::From(heap.get());
|
||||
|
||||
EXPECT_EQ(marker.stack_start(), internal_heap->stack()->stack_start());
|
||||
EXPECT_LT(internal_heap->stack()->stack_start(),
|
||||
v8::base::Stack::GetStackStart());
|
||||
}
|
||||
|
||||
TEST_F(StackStartMarkerTest, DefaultWithoutStackStartMarker) {
|
||||
Heap::HeapOptions options;
|
||||
EXPECT_FALSE(options.stack_start_marker.has_value());
|
||||
|
||||
auto heap = Heap::Create(GetPlatformHandle(), std::move(options));
|
||||
Heap* internal_heap = Heap::From(heap.get());
|
||||
|
||||
// Without a marker, it should use the default stack start.
|
||||
EXPECT_EQ(v8::base::Stack::GetStackStart(),
|
||||
internal_heap->stack()->stack_start());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace cppgc
|
||||
@@ -24,7 +24,9 @@ namespace {
|
||||
|
||||
class GCStackTest : public ::testing::Test {
|
||||
public:
|
||||
GCStackTest() : stack_(std::make_unique<Stack>()) { stack_->SetStackStart(); }
|
||||
GCStackTest() : stack_(std::make_unique<Stack>()) {
|
||||
stack_->SetStackStart(v8::base::Stack::GetStackStart());
|
||||
}
|
||||
|
||||
Stack* GetStack() const { return stack_.get(); }
|
||||
|
||||
|
||||
Reference in new issue
Block a user