v8/test/unittests/api/isolate-unittest.cc
ishell@chromium.org 4be9de90ac [api] Add v8::CrashKeyId::kCodeRangeBaseAddress
... 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}
2022-08-23 15:26:09 +00:00

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