marking-verifier-unittest.cc 9.18 KB
Newer Older
1 2 3 4 5 6 7 8 9
// 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.

#include "src/heap/cppgc/marking-verifier.h"

#include "include/cppgc/allocation.h"
#include "include/cppgc/member.h"
#include "include/cppgc/persistent.h"
10
#include "include/cppgc/prefinalizer.h"
11 12 13 14 15 16 17 18 19 20 21 22 23 24
#include "src/heap/cppgc/heap-object-header.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 MarkingVerifierTest : public testing::TestWithHeap {
 public:
  using StackState = Heap::Config::StackState;

25 26
  V8_NOINLINE void VerifyMarking(HeapBase& heap, StackState stack_state,
                                 size_t expected_marked_bytes) {
27
    Heap::From(GetHeap())->object_allocator().ResetLinearAllocationBuffers();
28
    MarkingVerifier verifier(heap);
29 30
    verifier.Run(stack_state, v8::base::Stack::GetCurrentStackPosition(),
                 expected_marked_bytes);
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
  }
};

class GCed : public GarbageCollected<GCed> {
 public:
  void SetChild(GCed* child) { child_ = child; }
  void SetWeakChild(GCed* child) { weak_child_ = child; }
  GCed* child() const { return child_.Get(); }
  GCed* weak_child() const { return weak_child_.Get(); }
  void Trace(cppgc::Visitor* visitor) const {
    visitor->Trace(child_);
    visitor->Trace(weak_child_);
  }

 private:
  Member<GCed> child_;
  WeakMember<GCed> weak_child_;
};

template <typename T>
V8_NOINLINE T access(volatile const T& t) {
  return t;
}

}  // namespace

// Following tests should not crash.

59
TEST_F(MarkingVerifierTest, DoesNotDieOnMarkedOnStackReference) {
60
  GCed* object = MakeGarbageCollected<GCed>(GetAllocationHandle());
61 62
  auto& header = HeapObjectHeader::FromObject(object);
  ASSERT_TRUE(header.TryMarkAtomic());
63
  VerifyMarking(Heap::From(GetHeap())->AsBase(),
64
                StackState::kMayContainHeapPointers, header.AllocatedSize());
65 66 67
  access(object);
}

68
TEST_F(MarkingVerifierTest, DoesNotDieOnMarkedMember) {
69
  Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetAllocationHandle());
70 71
  auto& parent_header = HeapObjectHeader::FromObject(parent.Get());
  ASSERT_TRUE(parent_header.TryMarkAtomic());
72
  parent->SetChild(MakeGarbageCollected<GCed>(GetAllocationHandle()));
73 74 75 76
  auto& child_header = HeapObjectHeader::FromObject(parent->child());
  ASSERT_TRUE(child_header.TryMarkAtomic());
  VerifyMarking(Heap::From(GetHeap())->AsBase(), StackState::kNoHeapPointers,
                parent_header.AllocatedSize() + child_header.AllocatedSize());
77 78
}

79
TEST_F(MarkingVerifierTest, DoesNotDieOnMarkedWeakMember) {
80
  Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetAllocationHandle());
81 82
  auto& parent_header = HeapObjectHeader::FromObject(parent.Get());
  ASSERT_TRUE(parent_header.TryMarkAtomic());
83
  parent->SetWeakChild(MakeGarbageCollected<GCed>(GetAllocationHandle()));
84 85 86 87
  auto& child_header = HeapObjectHeader::FromObject(parent->weak_child());
  ASSERT_TRUE(child_header.TryMarkAtomic());
  VerifyMarking(Heap::From(GetHeap())->AsBase(), StackState::kNoHeapPointers,
                parent_header.AllocatedSize() + child_header.AllocatedSize());
88 89
}

90 91 92 93 94 95 96 97 98 99 100 101 102
namespace {

class GCedWithCallback : public GarbageCollected<GCedWithCallback> {
 public:
  template <typename Callback>
  explicit GCedWithCallback(Callback callback) {
    callback(this);
  }
  void Trace(cppgc::Visitor* visitor) const {}
};

}  // namespace

103
TEST_F(MarkingVerifierTest, DoesNotDieOnInConstructionOnObject) {
104 105
  MakeGarbageCollected<GCedWithCallback>(
      GetAllocationHandle(), [this](GCedWithCallback* obj) {
106 107
        auto& header = HeapObjectHeader::FromObject(obj);
        CHECK(header.TryMarkAtomic());
108
        VerifyMarking(Heap::From(GetHeap())->AsBase(),
109 110
                      StackState::kMayContainHeapPointers,
                      header.AllocatedSize());
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
namespace {
class GCedWithCallbackAndChild final
    : public GarbageCollected<GCedWithCallbackAndChild> {
 public:
  template <typename Callback>
  GCedWithCallbackAndChild(GCed* gced, Callback callback) : child_(gced) {
    callback(this);
  }
  void Trace(cppgc::Visitor* visitor) const { visitor->Trace(child_); }

 private:
  Member<GCed> child_;
};

template <typename T>
struct Holder : public GarbageCollected<Holder<T>> {
 public:
  void Trace(cppgc::Visitor* visitor) const { visitor->Trace(object); }
  Member<T> object = nullptr;
};
}  // namespace

TEST_F(MarkingVerifierTest, DoesntDieOnInConstructionObjectWithWriteBarrier) {
137
  // Regression test: https://crbug.com/v8/10989.
138
  // GCedWithCallbackAndChild is marked by write barrier and then discarded by
139
  // FlushNotFullyConstructedObjects because it is already marked.
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
  Persistent<Holder<GCedWithCallbackAndChild>> persistent =
      MakeGarbageCollected<Holder<GCedWithCallbackAndChild>>(
          GetAllocationHandle());
  GarbageCollector::Config config =
      GarbageCollector::Config::PreciseIncrementalConfig();
  Heap::From(GetHeap())->StartIncrementalGarbageCollection(config);
  MakeGarbageCollected<GCedWithCallbackAndChild>(
      GetAllocationHandle(), MakeGarbageCollected<GCed>(GetAllocationHandle()),
      [&persistent](GCedWithCallbackAndChild* obj) {
        persistent->object = obj;
      });
  GetMarkerRef()->IncrementalMarkingStepForTesting(
      GarbageCollector::Config::StackState::kNoHeapPointers);
  Heap::From(GetHeap())->FinalizeIncrementalGarbageCollectionIfRunning(config);
}

156 157 158 159
// Death tests.

namespace {

160 161 162 163 164
class MarkingVerifierDeathTest : public MarkingVerifierTest {
 protected:
  template <template <typename T> class Reference>
  void TestResurrectingPreFinalizer();
};
165 166 167 168 169

}  // namespace

TEST_F(MarkingVerifierDeathTest, DieOnUnmarkedOnStackReference) {
  GCed* object = MakeGarbageCollected<GCed>(GetAllocationHandle());
170 171
  auto& header = HeapObjectHeader::FromObject(object);
  USE(header);
172
  EXPECT_DEATH_IF_SUPPORTED(VerifyMarking(Heap::From(GetHeap())->AsBase(),
173 174
                                          StackState::kMayContainHeapPointers,
                                          header.AllocatedSize()),
175 176 177 178 179 180
                            "");
  access(object);
}

TEST_F(MarkingVerifierDeathTest, DieOnUnmarkedMember) {
  Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetAllocationHandle());
181 182
  auto& parent_header = HeapObjectHeader::FromObject(parent);
  ASSERT_TRUE(parent_header.TryMarkAtomic());
183
  parent->SetChild(MakeGarbageCollected<GCed>(GetAllocationHandle()));
184 185 186 187
  EXPECT_DEATH_IF_SUPPORTED(
      VerifyMarking(Heap::From(GetHeap())->AsBase(),
                    StackState::kNoHeapPointers, parent_header.AllocatedSize()),
      "");
188 189 190 191
}

TEST_F(MarkingVerifierDeathTest, DieOnUnmarkedWeakMember) {
  Persistent<GCed> parent = MakeGarbageCollected<GCed>(GetAllocationHandle());
192 193
  auto& parent_header = HeapObjectHeader::FromObject(parent);
  ASSERT_TRUE(parent_header.TryMarkAtomic());
194
  parent->SetWeakChild(MakeGarbageCollected<GCed>(GetAllocationHandle()));
195 196 197 198 199 200 201 202 203 204 205 206
  EXPECT_DEATH_IF_SUPPORTED(
      VerifyMarking(Heap::From(GetHeap())->AsBase(),
                    StackState::kNoHeapPointers, parent_header.AllocatedSize()),
      "");
}

#ifdef CPPGC_VERIFY_LIVE_BYTES

TEST_F(MarkingVerifierDeathTest, DieOnUnexpectedLiveByteCount) {
  GCed* object = MakeGarbageCollected<GCed>(GetAllocationHandle());
  auto& header = HeapObjectHeader::FromObject(object);
  ASSERT_TRUE(header.TryMarkAtomic());
207
  EXPECT_DEATH_IF_SUPPORTED(VerifyMarking(Heap::From(GetHeap())->AsBase(),
208 209
                                          StackState::kMayContainHeapPointers,
                                          header.AllocatedSize() - 1),
210 211 212
                            "");
}

213 214
#endif  // CPPGC_VERIFY_LIVE_BYTES

215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
namespace {

template <template <typename T> class Reference>
class ResurrectingPreFinalizer
    : public GarbageCollected<ResurrectingPreFinalizer<Reference>> {
  CPPGC_USING_PRE_FINALIZER(ResurrectingPreFinalizer<Reference>, Dispose);

 public:
  class Storage : public GarbageCollected<Storage> {
   public:
    void Trace(Visitor* visitor) const { visitor->Trace(ref); }

    Reference<GCed> ref;
  };

  ResurrectingPreFinalizer(Storage* storage, GCed* object_that_dies)
      : storage_(storage), object_that_dies_(object_that_dies) {}

  void Trace(Visitor* visitor) const {
    visitor->Trace(storage_);
    visitor->Trace(object_that_dies_);
  }

 private:
  void Dispose() { storage_->ref = object_that_dies_; }

  Member<Storage> storage_;
  Member<GCed> object_that_dies_;
};

}  // namespace

template <template <typename T> class Reference>
void MarkingVerifierDeathTest::TestResurrectingPreFinalizer() {
  Persistent<typename ResurrectingPreFinalizer<Reference>::Storage> storage(
      MakeGarbageCollected<
          typename ResurrectingPreFinalizer<Reference>::Storage>(
          GetAllocationHandle()));
  MakeGarbageCollected<ResurrectingPreFinalizer<Reference>>(
      GetAllocationHandle(), storage.Get(),
      MakeGarbageCollected<GCed>(GetAllocationHandle()));
  EXPECT_DEATH_IF_SUPPORTED(PreciseGC(), "");
}
258

259
#if DEBUG
260

261 262 263 264 265 266 267
TEST_F(MarkingVerifierDeathTest, DiesOnResurrectedMember) {
  TestResurrectingPreFinalizer<Member>();
}

TEST_F(MarkingVerifierDeathTest, DiesOnResurrectedWeakMember) {
  TestResurrectingPreFinalizer<WeakMember>();
}
268

269 270
#endif  // DEBUG

271 272
}  // namespace internal
}  // namespace cppgc