2017-02-08 09:38:50 +00:00
|
|
|
// 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.
|
|
|
|
|
2021-08-23 13:01:06 +00:00
|
|
|
#include "src/execution/isolate.h"
|
2017-02-08 09:38:50 +00:00
|
|
|
|
|
|
|
#include "include/libplatform/libplatform.h"
|
|
|
|
#include "include/v8-platform.h"
|
2021-08-23 13:01:06 +00:00
|
|
|
#include "include/v8-template.h"
|
2017-02-08 09:38:50 +00:00
|
|
|
#include "src/base/platform/semaphore.h"
|
2019-05-24 13:51:59 +00:00
|
|
|
#include "src/init/v8.h"
|
2017-02-08 09:38:50 +00:00
|
|
|
#include "test/unittests/test-utils.h"
|
2021-08-23 13:01:06 +00:00
|
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
2017-02-08 09:38:50 +00:00
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
|
2019-05-27 11:31:49 +00:00
|
|
|
using IsolateTest = TestWithIsolate;
|
2017-02-08 09:38:50 +00:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class MemoryPressureTask : public v8::Task {
|
|
|
|
public:
|
|
|
|
MemoryPressureTask(Isolate* isolate, base::Semaphore* semaphore)
|
|
|
|
: isolate_(isolate), semaphore_(semaphore) {}
|
|
|
|
~MemoryPressureTask() override = default;
|
2020-11-05 08:11:05 +00:00
|
|
|
MemoryPressureTask(const MemoryPressureTask&) = delete;
|
|
|
|
MemoryPressureTask& operator=(const MemoryPressureTask&) = delete;
|
2017-02-08 09:38:50 +00:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
2018-03-01 08:45:52 +00:00
|
|
|
internal::V8::GetCurrentPlatform()->CallOnWorkerThread(
|
2019-09-10 10:12:00 +00:00
|
|
|
std::make_unique<MemoryPressureTask>(isolate(), &semaphore));
|
2017-02-08 09:38:50 +00:00
|
|
|
|
|
|
|
semaphore.Wait();
|
|
|
|
|
|
|
|
ASSERT_TRUE(i_isolate->stack_guard()->CheckGC());
|
|
|
|
v8::platform::PumpMessageLoop(internal::V8::GetCurrentPlatform(), isolate());
|
|
|
|
}
|
|
|
|
|
2018-09-13 15:50:38 +00:00
|
|
|
using IncumbentContextTest = TestWithIsolate;
|
|
|
|
|
|
|
|
// Check that Isolate::GetIncumbentContext() returns the correct one in basic
|
|
|
|
// scenarios.
|
2018-11-20 14:32:42 +00:00
|
|
|
TEST_F(IncumbentContextTest, Basic) {
|
2018-09-13 15:50:38 +00:00
|
|
|
auto Str = [&](const char* s) {
|
2020-03-09 10:41:45 +00:00
|
|
|
return String::NewFromUtf8(isolate(), s).ToLocalChecked();
|
2018-09-13 15:50:38 +00:00
|
|
|
};
|
|
|
|
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());
|
2020-09-16 09:54:02 +00:00
|
|
|
global_template->Set(isolate(), "getIncumbentGlobal", get_incumbent_global);
|
2018-09-13 15:50:38 +00:00
|
|
|
|
|
|
|
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()"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-28 13:18:48 +00:00
|
|
|
namespace {
|
2022-08-23 10:54:34 +00:00
|
|
|
thread_local std::multimap<v8::CrashKeyId, std::string> crash_keys;
|
2022-04-28 13:18:48 +00:00
|
|
|
void CrashKeyCallback(v8::CrashKeyId id, const std::string& value) {
|
2022-08-23 10:54:34 +00:00
|
|
|
crash_keys.insert({id, value});
|
2022-04-28 13:18:48 +00:00
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_F(IsolateTest, SetAddCrashKeyCallback) {
|
|
|
|
isolate()->SetAddCrashKeyCallback(CrashKeyCallback);
|
2022-05-24 10:53:14 +00:00
|
|
|
|
2022-08-23 10:54:34 +00:00
|
|
|
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);
|
2022-04-28 13:18:48 +00:00
|
|
|
}
|
|
|
|
|
2017-02-08 09:38:50 +00:00
|
|
|
} // namespace v8
|