4be9de90ac
... when the code range is created. This key should be more helpful than the existing kCodeSpaceFirstPageAddress crash key, especially for the cases when snapshot does not contain Code objects and thus the code space is not created during Isolate initialization. The mid-term plan is to remove the latter in favour of the former since the default configuration does not imply creation of the code space. Bug: v8:11880 Change-Id: Icdea38723c7ed73605c2df6589ec01193571d55c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3849038 Reviewed-by: Michael Lippautz <mlippautz@chromium.org> Commit-Queue: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/main@{#82667}
171 lines
6.0 KiB
C++
171 lines
6.0 KiB
C++
// Copyright 2017 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/execution/isolate.h"
|
|
|
|
#include "include/libplatform/libplatform.h"
|
|
#include "include/v8-platform.h"
|
|
#include "include/v8-template.h"
|
|
#include "src/base/platform/semaphore.h"
|
|
#include "src/init/v8.h"
|
|
#include "test/unittests/test-utils.h"
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
|
|
|
namespace v8 {
|
|
|
|
using IsolateTest = TestWithIsolate;
|
|
|
|
namespace {
|
|
|
|
class MemoryPressureTask : public v8::Task {
|
|
public:
|
|
MemoryPressureTask(Isolate* isolate, base::Semaphore* semaphore)
|
|
: isolate_(isolate), semaphore_(semaphore) {}
|
|
~MemoryPressureTask() override = default;
|
|
MemoryPressureTask(const MemoryPressureTask&) = delete;
|
|
MemoryPressureTask& operator=(const MemoryPressureTask&) = delete;
|
|
|
|
// v8::Task implementation.
|
|
void Run() override {
|
|
isolate_->MemoryPressureNotification(MemoryPressureLevel::kCritical);
|
|
semaphore_->Signal();
|
|
}
|
|
|
|
private:
|
|
Isolate* isolate_;
|
|
base::Semaphore* semaphore_;
|
|
};
|
|
|
|
} // namespace
|
|
|
|
// Check that triggering a memory pressure notification on the isolate thread
|
|
// doesn't request a GC interrupt.
|
|
TEST_F(IsolateTest, MemoryPressureNotificationForeground) {
|
|
internal::Isolate* i_isolate =
|
|
reinterpret_cast<internal::Isolate*>(isolate());
|
|
|
|
ASSERT_FALSE(i_isolate->stack_guard()->CheckGC());
|
|
isolate()->MemoryPressureNotification(MemoryPressureLevel::kCritical);
|
|
ASSERT_FALSE(i_isolate->stack_guard()->CheckGC());
|
|
}
|
|
|
|
// Check that triggering a memory pressure notification on an background thread
|
|
// requests a GC interrupt.
|
|
TEST_F(IsolateTest, MemoryPressureNotificationBackground) {
|
|
internal::Isolate* i_isolate =
|
|
reinterpret_cast<internal::Isolate*>(isolate());
|
|
|
|
base::Semaphore semaphore(0);
|
|
|
|
internal::V8::GetCurrentPlatform()->CallOnWorkerThread(
|
|
std::make_unique<MemoryPressureTask>(isolate(), &semaphore));
|
|
|
|
semaphore.Wait();
|
|
|
|
ASSERT_TRUE(i_isolate->stack_guard()->CheckGC());
|
|
v8::platform::PumpMessageLoop(internal::V8::GetCurrentPlatform(), isolate());
|
|
}
|
|
|
|
using IncumbentContextTest = TestWithIsolate;
|
|
|
|
// Check that Isolate::GetIncumbentContext() returns the correct one in basic
|
|
// scenarios.
|
|
TEST_F(IncumbentContextTest, Basic) {
|
|
auto Str = [&](const char* s) {
|
|
return String::NewFromUtf8(isolate(), s).ToLocalChecked();
|
|
};
|
|
auto Run = [&](Local<Context> context, const char* script) {
|
|
Context::Scope scope(context);
|
|
return Script::Compile(context, Str(script))
|
|
.ToLocalChecked()
|
|
->Run(context)
|
|
.ToLocalChecked();
|
|
};
|
|
|
|
// Set up the test environment; three contexts with getIncumbentGlobal()
|
|
// function.
|
|
Local<FunctionTemplate> get_incumbent_global = FunctionTemplate::New(
|
|
isolate(), [](const FunctionCallbackInfo<Value>& info) {
|
|
Local<Context> incumbent_context =
|
|
info.GetIsolate()->GetIncumbentContext();
|
|
info.GetReturnValue().Set(incumbent_context->Global());
|
|
});
|
|
Local<ObjectTemplate> global_template = ObjectTemplate::New(isolate());
|
|
global_template->Set(isolate(), "getIncumbentGlobal", get_incumbent_global);
|
|
|
|
Local<Context> context_a = Context::New(isolate(), nullptr, global_template);
|
|
Local<Context> context_b = Context::New(isolate(), nullptr, global_template);
|
|
Local<Context> context_c = Context::New(isolate(), nullptr, global_template);
|
|
Local<Object> global_a = context_a->Global();
|
|
Local<Object> global_b = context_b->Global();
|
|
Local<Object> global_c = context_c->Global();
|
|
|
|
Local<String> security_token = Str("security_token");
|
|
context_a->SetSecurityToken(security_token);
|
|
context_b->SetSecurityToken(security_token);
|
|
context_c->SetSecurityToken(security_token);
|
|
|
|
global_a->Set(context_a, Str("b"), global_b).ToChecked();
|
|
global_b->Set(context_b, Str("c"), global_c).ToChecked();
|
|
|
|
// Test scenario 2: A -> B -> C, then the incumbent is C.
|
|
Run(context_a, "funcA = function() { return b.funcB(); }");
|
|
Run(context_b, "funcB = function() { return c.getIncumbentGlobal(); }");
|
|
// Without BackupIncumbentScope.
|
|
EXPECT_EQ(global_b, Run(context_a, "funcA()"));
|
|
{
|
|
// With BackupIncumbentScope.
|
|
Context::BackupIncumbentScope backup_incumbent(context_a);
|
|
EXPECT_EQ(global_b, Run(context_a, "funcA()"));
|
|
}
|
|
|
|
// Test scenario 2: A -> B -> C -> C, then the incumbent is C.
|
|
Run(context_a, "funcA = function() { return b.funcB(); }");
|
|
Run(context_b, "funcB = function() { return c.funcC(); }");
|
|
Run(context_c, "funcC = function() { return getIncumbentGlobal(); }");
|
|
// Without BackupIncumbentScope.
|
|
EXPECT_EQ(global_c, Run(context_a, "funcA()"));
|
|
{
|
|
// With BackupIncumbentScope.
|
|
Context::BackupIncumbentScope backup_incumbent(context_a);
|
|
EXPECT_EQ(global_c, Run(context_a, "funcA()"));
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
thread_local std::multimap<v8::CrashKeyId, std::string> crash_keys;
|
|
void CrashKeyCallback(v8::CrashKeyId id, const std::string& value) {
|
|
crash_keys.insert({id, value});
|
|
}
|
|
} // namespace
|
|
TEST_F(IsolateTest, SetAddCrashKeyCallback) {
|
|
isolate()->SetAddCrashKeyCallback(CrashKeyCallback);
|
|
|
|
i::Isolate* i_isolate = reinterpret_cast<internal::Isolate*>(isolate());
|
|
i::Heap* heap = i_isolate->heap();
|
|
|
|
size_t expected_keys_count = 4;
|
|
EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kIsolateAddress), 1u);
|
|
EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kReadonlySpaceFirstPageAddress),
|
|
1u);
|
|
EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kSnapshotChecksumCalculated), 1u);
|
|
EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kSnapshotChecksumExpected), 1u);
|
|
|
|
if (heap->map_space()) {
|
|
++expected_keys_count;
|
|
EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kMapSpaceFirstPageAddress), 1u);
|
|
}
|
|
if (heap->code_range_base()) {
|
|
++expected_keys_count;
|
|
EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kCodeRangeBaseAddress), 1u);
|
|
}
|
|
if (heap->code_space()->first_page()) {
|
|
++expected_keys_count;
|
|
EXPECT_EQ(crash_keys.count(v8::CrashKeyId::kCodeSpaceFirstPageAddress), 1u);
|
|
}
|
|
EXPECT_EQ(crash_keys.size(), expected_keys_count);
|
|
}
|
|
|
|
} // namespace v8
|