[wasm] Call FatalProcessOutOfMemory on OOM

Instead of returning nullptr, just always call FatalProcessOutOfMemory
when we cannot allocate more memory.
In a follow-up CL, this should be extended to first try to run a GC and
see if this freed enough memory.
This CL is intentionally minimal in order to make it backmergable.

The unittest for WasmCodeManager needs to be refactored into a
parameterized test, such that each individual (parameterized) test can
die with OOM without affecting other tests.

R=mstarzinger@chromium.org

Bug: chromium:822266
Change-Id: I1336aa05ed50124b77ffaa4435ec9bed70e15c18
Reviewed-on: https://chromium-review.googlesource.com/966501
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52025}
This commit is contained in:
Clemens Hammacher 2018-03-19 13:34:59 +01:00 committed by Commit Bot
parent c15802e11e
commit 468a9303fd
4 changed files with 170 additions and 190 deletions

View File

@ -871,6 +871,10 @@ void OS::Sleep(TimeDelta interval) {
void OS::Abort() {
// Before aborting, make sure to flush output buffers.
fflush(stdout);
fflush(stderr);
if (g_hard_abort) {
V8_IMMEDIATE_CRASH();
}

View File

@ -327,7 +327,10 @@ WasmCode* NativeModule::AddOwnedCode(
// section, thus ensuring owned_code_'s elements are rarely if ever moved.
base::LockGuard<base::Mutex> lock(&allocation_mutex_);
Address executable_buffer = AllocateForCode(orig_instructions.size());
if (executable_buffer == nullptr) return nullptr;
if (executable_buffer == nullptr) {
V8::FatalProcessOutOfMemory("NativeModule::AddOwnedCode");
UNREACHABLE();
}
memcpy(executable_buffer, orig_instructions.start(),
orig_instructions.size());
std::unique_ptr<WasmCode> code(new WasmCode(
@ -502,7 +505,6 @@ Address NativeModule::CreateTrampolineTo(Handle<Code> code) {
{code_desc.buffer, static_cast<size_t>(code_desc.instr_size)}, nullptr, 0,
Nothing<uint32_t>(), WasmCode::kTrampoline, 0, 0, 0, 0, {},
WasmCode::kOther);
if (wasm_code == nullptr) return nullptr;
Address ret = wasm_code->instructions().start();
trampolines_.emplace(std::make_pair(dest, ret));
return ret;
@ -644,21 +646,20 @@ WasmCode* NativeModule::CloneLazyBuiltinInto(const WasmCode* code,
return ret;
}
bool NativeModule::CloneTrampolinesAndStubs(const NativeModule* other) {
void NativeModule::CloneTrampolinesAndStubs(const NativeModule* other) {
for (auto& pair : other->trampolines_) {
Address key = pair.first;
Address local =
GetLocalAddressFor(handle(Code::GetCodeFromTargetAddress(key)));
if (local == nullptr) return false;
DCHECK_NOT_NULL(local);
trampolines_.emplace(std::make_pair(key, local));
}
for (auto& pair : other->stubs_) {
uint32_t key = pair.first;
WasmCode* clone = CloneCode(pair.second);
if (!clone) return false;
DCHECK_NOT_NULL(clone);
stubs_.emplace(std::make_pair(key, clone));
}
return true;
}
WasmCode* NativeModule::CloneCode(const WasmCode* original_code) {
@ -676,7 +677,6 @@ WasmCode* NativeModule::CloneCode(const WasmCode* original_code) {
original_code->handler_table_offset_,
original_code->protected_instructions_, original_code->tier(),
false /* flush_icache */);
if (ret == nullptr) return nullptr;
if (!ret->IsAnonymous()) {
code_table_[ret->index()] = ret;
}
@ -872,7 +872,7 @@ std::unique_ptr<NativeModule> NativeModule::Clone() {
TRACE_HEAP("%zu cloned from %zu\n", ret->instance_id, instance_id);
if (!ret) return ret;
if (!ret->CloneTrampolinesAndStubs(this)) return nullptr;
ret->CloneTrampolinesAndStubs(this);
std::unordered_map<Address, Address, AddressHasher> reverse_lookup;
for (auto& pair : trampolines_) {

View File

@ -295,7 +295,7 @@ class V8_EXPORT_PRIVATE NativeModule final {
std::shared_ptr<ProtectedInstructions>,
WasmCode::Tier tier, bool flush_icache = true);
WasmCode* CloneCode(const WasmCode*);
bool CloneTrampolinesAndStubs(const NativeModule* other);
void CloneTrampolinesAndStubs(const NativeModule* other);
WasmCode* Lookup(Address);
Address GetLocalAddressFor(Handle<Code>);
Address CreateTrampolineTo(Handle<Code>);

View File

@ -151,14 +151,13 @@ TEST_F(DisjointAllocationPoolTest, MergingSkipLargerSrcWithGap) {
CheckLooksLike(a, {{10, 15}, {20, 35}, {36, 40}});
}
class WasmCodeManagerTest : public TestWithContext {
enum ModuleStyle : int { Fixed = 0, Growable = 1 };
class WasmCodeManagerTest : public TestWithContext,
public ::testing::WithParamInterface<ModuleStyle> {
public:
using NativeModulePtr = std::unique_ptr<NativeModule>;
enum ModuleStyle : int { Fixed = 0, Growable = 1 };
const std::vector<ModuleStyle> styles() const {
return std::vector<ModuleStyle>({Fixed, Growable});
}
// We pretend all our modules have 10 functions and no imports, just so
// we can size up the code_table.
NativeModulePtr AllocFixedModule(WasmCodeManager* manager, size_t size) {
@ -196,194 +195,171 @@ class WasmCodeManagerTest : public TestWithContext {
}
};
TEST_F(WasmCodeManagerTest, EmptyCase) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 0 * page());
CHECK_EQ(0, manager.remaining_uncommitted());
INSTANTIATE_TEST_CASE_P(Parameterized, WasmCodeManagerTest,
::testing::Values(Fixed, Growable));
NativeModulePtr native_module = AllocModule(&manager, 1 * page(), style);
CHECK(native_module);
WasmCode* code = AddCode(native_module.get(), 0, 10);
CHECK_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
native_module.reset();
CHECK_EQ(0, manager.remaining_uncommitted());
TEST_P(WasmCodeManagerTest, EmptyCase) {
WasmCodeManager manager(v8_isolate(), 0 * page());
CHECK_EQ(0, manager.remaining_uncommitted());
NativeModulePtr native_module = AllocModule(&manager, 1 * page(), GetParam());
CHECK(native_module);
ASSERT_DEATH_IF_SUPPORTED(AddCode(native_module.get(), 0, 10),
"OOM in NativeModule::AddOwnedCode");
}
TEST_P(WasmCodeManagerTest, AllocateAndGoOverLimit) {
WasmCodeManager manager(v8_isolate(), 1 * page());
CHECK_EQ(1 * page(), manager.remaining_uncommitted());
NativeModulePtr native_module = AllocModule(&manager, 1 * page(), GetParam());
CHECK(native_module);
CHECK_EQ(1 * page(), manager.remaining_uncommitted());
uint32_t index = 0;
WasmCode* code = AddCode(native_module.get(), index++, 1 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
code = AddCode(native_module.get(), index++, 3 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
code = AddCode(native_module.get(), index++, page() - 4 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
ASSERT_DEATH_IF_SUPPORTED(
AddCode(native_module.get(), index++, 1 * kCodeAlignment),
"OOM in NativeModule::AddOwnedCode");
}
TEST_P(WasmCodeManagerTest, TotalLimitIrrespectiveOfModuleCount) {
WasmCodeManager manager(v8_isolate(), 1 * page());
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), GetParam());
NativeModulePtr nm2 = AllocModule(&manager, 1 * page(), GetParam());
CHECK(nm1);
CHECK(nm2);
WasmCode* code = AddCode(nm1.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
ASSERT_DEATH_IF_SUPPORTED(AddCode(nm2.get(), 0, 1 * page()),
"OOM in NativeModule::AddOwnedCode");
}
TEST_P(WasmCodeManagerTest, DifferentHeapsApplyLimitsIndependently) {
WasmCodeManager manager1(v8_isolate(), 1 * page());
WasmCodeManager manager2(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager1, 1 * page(), GetParam());
NativeModulePtr nm2 = AllocModule(&manager2, 1 * page(), GetParam());
CHECK(nm1);
CHECK(nm2);
WasmCode* code = AddCode(nm1.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager1.remaining_uncommitted());
code = AddCode(nm2.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
}
TEST_P(WasmCodeManagerTest, GrowingVsFixedModule) {
WasmCodeManager manager(v8_isolate(), 3 * page());
NativeModulePtr nm = AllocModule(&manager, 1 * page(), GetParam());
if (GetParam() == Fixed) {
ASSERT_DEATH_IF_SUPPORTED(AddCode(nm.get(), 0, 1 * page() + kCodeAlignment),
"OOM in NativeModule::AddOwnedCode");
} else {
CHECK_NOT_NULL(AddCode(nm.get(), 0, 1 * page() + kCodeAlignment));
CHECK_EQ(manager.remaining_uncommitted(), 1 * page());
}
}
TEST_F(WasmCodeManagerTest, AllocateAndGoOverLimit) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 1 * page());
CHECK_EQ(1 * page(), manager.remaining_uncommitted());
NativeModulePtr native_module = AllocModule(&manager, 1 * page(), style);
CHECK(native_module);
CHECK_EQ(1 * page(), manager.remaining_uncommitted());
uint32_t index = 0;
WasmCode* code = AddCode(native_module.get(), index++, 1 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
code = AddCode(native_module.get(), index++, 3 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
code = AddCode(native_module.get(), index++, page() - 4 * kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
code = AddCode(native_module.get(), index++, 1 * kCodeAlignment);
CHECK_NULL(code);
CHECK_EQ(0, manager.remaining_uncommitted());
native_module.reset();
CHECK_EQ(1 * page(), manager.remaining_uncommitted());
}
TEST_P(WasmCodeManagerTest, CommitIncrements) {
WasmCodeManager manager(v8_isolate(), 10 * page());
NativeModulePtr nm = AllocModule(&manager, 3 * page(), GetParam());
WasmCode* code = AddCode(nm.get(), 0, kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 9 * page());
code = AddCode(nm.get(), 1, 2 * page());
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 7 * page());
code = AddCode(nm.get(), 2, page() - kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 7 * page());
}
TEST_F(WasmCodeManagerTest, TotalLimitIrrespectiveOfModuleCount) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 1 * page());
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), style);
NativeModulePtr nm2 = AllocModule(&manager, 1 * page(), style);
CHECK(nm1);
CHECK(nm2);
WasmCode* code = AddCode(nm1.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
code = AddCode(nm2.get(), 0, 1 * page());
CHECK_NULL(code);
}
TEST_P(WasmCodeManagerTest, Lookup) {
WasmCodeManager manager(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), GetParam());
NativeModulePtr nm2 = AllocModule(&manager, 1 * page(), GetParam());
WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
CHECK_EQ(nm1.get(), code1_0->native_module());
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
CHECK_EQ(nm2.get(), code2_1->native_module());
CHECK_EQ(0, code1_0->index());
CHECK_EQ(1, code1_1->index());
CHECK_EQ(0, code2_0->index());
CHECK_EQ(1, code2_1->index());
// we know the manager object is allocated here, so we shouldn't
// find any WasmCode* associated with that ptr.
WasmCode* not_found = manager.LookupCode(reinterpret_cast<Address>(&manager));
CHECK_NULL(not_found);
WasmCode* found = manager.LookupCode(code1_0->instructions().start());
CHECK_EQ(found, code1_0);
found = manager.LookupCode(code2_1->instructions().start() +
(code2_1->instructions().size() / 2));
CHECK_EQ(found, code2_1);
found = manager.LookupCode(code2_1->instructions().start() +
code2_1->instructions().size() - 1);
CHECK_EQ(found, code2_1);
found = manager.LookupCode(code2_1->instructions().start() +
code2_1->instructions().size());
CHECK_NULL(found);
Address mid_code1_1 =
code1_1->instructions().start() + (code1_1->instructions().size() / 2);
CHECK_EQ(code1_1, manager.LookupCode(mid_code1_1));
nm1.reset();
CHECK_NULL(manager.LookupCode(mid_code1_1));
}
TEST_F(WasmCodeManagerTest, DifferentHeapsApplyLimitsIndependently) {
for (auto style : styles()) {
WasmCodeManager manager1(v8_isolate(), 1 * page());
WasmCodeManager manager2(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager1, 1 * page(), style);
NativeModulePtr nm2 = AllocModule(&manager2, 1 * page(), style);
CHECK(nm1);
CHECK(nm2);
WasmCode* code = AddCode(nm1.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
CHECK_EQ(0, manager1.remaining_uncommitted());
code = AddCode(nm2.get(), 0, 1 * page());
CHECK_NOT_NULL(code);
}
TEST_P(WasmCodeManagerTest, MultiManagerLookup) {
WasmCodeManager manager1(v8_isolate(), 2 * page());
WasmCodeManager manager2(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager1, 1 * page(), GetParam());
NativeModulePtr nm2 = AllocModule(&manager2, 1 * page(), GetParam());
WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
CHECK_EQ(nm1.get(), code1_0->native_module());
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
CHECK_EQ(nm2.get(), code2_1->native_module());
CHECK_EQ(0, code1_0->index());
CHECK_EQ(1, code1_1->index());
CHECK_EQ(0, code2_0->index());
CHECK_EQ(1, code2_1->index());
CHECK_EQ(code1_0, manager1.LookupCode(code1_0->instructions().start()));
CHECK_NULL(manager2.LookupCode(code1_0->instructions().start()));
}
TEST_F(WasmCodeManagerTest, GrowingVsFixedModule) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 3 * page());
NativeModulePtr nm = AllocModule(&manager, 1 * page(), style);
WasmCode* code = AddCode(nm.get(), 0, 1 * page() + kCodeAlignment);
if (style == Fixed) {
CHECK_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 3 * page());
} else {
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 1 * page());
}
}
}
TEST_P(WasmCodeManagerTest, LookupWorksAfterRewrite) {
WasmCodeManager manager(v8_isolate(), 2 * page());
TEST_F(WasmCodeManagerTest, CommitIncrements) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 10 * page());
NativeModulePtr nm = AllocModule(&manager, 3 * page(), style);
WasmCode* code = AddCode(nm.get(), 0, kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 9 * page());
code = AddCode(nm.get(), 1, 2 * page());
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 7 * page());
code = AddCode(nm.get(), 2, page() - kCodeAlignment);
CHECK_NOT_NULL(code);
CHECK_EQ(manager.remaining_uncommitted(), 7 * page());
}
}
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), GetParam());
TEST_F(WasmCodeManagerTest, Lookup) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), style);
NativeModulePtr nm2 = AllocModule(&manager, 1 * page(), style);
WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
CHECK_EQ(nm1.get(), code1_0->native_module());
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
CHECK_EQ(nm2.get(), code2_1->native_module());
CHECK_EQ(0, code1_0->index());
CHECK_EQ(1, code1_1->index());
CHECK_EQ(0, code2_0->index());
CHECK_EQ(1, code2_1->index());
// we know the manager object is allocated here, so we shouldn't
// find any WasmCode* associated with that ptr.
WasmCode* not_found =
manager.LookupCode(reinterpret_cast<Address>(&manager));
CHECK_NULL(not_found);
WasmCode* found = manager.LookupCode(code1_0->instructions().start());
CHECK_EQ(found, code1_0);
found = manager.LookupCode(code2_1->instructions().start() +
(code2_1->instructions().size() / 2));
CHECK_EQ(found, code2_1);
found = manager.LookupCode(code2_1->instructions().start() +
code2_1->instructions().size() - 1);
CHECK_EQ(found, code2_1);
found = manager.LookupCode(code2_1->instructions().start() +
code2_1->instructions().size());
CHECK_NULL(found);
Address mid_code1_1 =
code1_1->instructions().start() + (code1_1->instructions().size() / 2);
CHECK_EQ(code1_1, manager.LookupCode(mid_code1_1));
nm1.reset();
CHECK_NULL(manager.LookupCode(mid_code1_1));
}
}
TEST_F(WasmCodeManagerTest, MultiManagerLookup) {
for (auto style : styles()) {
WasmCodeManager manager1(v8_isolate(), 2 * page());
WasmCodeManager manager2(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager1, 1 * page(), style);
NativeModulePtr nm2 = AllocModule(&manager2, 1 * page(), style);
WasmCode* code1_0 = AddCode(nm1.get(), 0, kCodeAlignment);
CHECK_EQ(nm1.get(), code1_0->native_module());
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
WasmCode* code2_0 = AddCode(nm2.get(), 0, kCodeAlignment);
WasmCode* code2_1 = AddCode(nm2.get(), 1, kCodeAlignment);
CHECK_EQ(nm2.get(), code2_1->native_module());
CHECK_EQ(0, code1_0->index());
CHECK_EQ(1, code1_1->index());
CHECK_EQ(0, code2_0->index());
CHECK_EQ(1, code2_1->index());
CHECK_EQ(code1_0, manager1.LookupCode(code1_0->instructions().start()));
CHECK_NULL(manager2.LookupCode(code1_0->instructions().start()));
}
}
TEST_F(WasmCodeManagerTest, LookupWorksAfterRewrite) {
for (auto style : styles()) {
WasmCodeManager manager(v8_isolate(), 2 * page());
NativeModulePtr nm1 = AllocModule(&manager, 1 * page(), style);
WasmCode* code0 = AddCode(nm1.get(), 0, kCodeAlignment);
WasmCode* code1 = AddCode(nm1.get(), 1, kCodeAlignment);
CHECK_EQ(0, code0->index());
CHECK_EQ(1, code1->index());
CHECK_EQ(code1, manager.LookupCode(code1->instructions().start()));
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
CHECK_EQ(1, code1_1->index());
CHECK_EQ(code1, manager.LookupCode(code1->instructions().start()));
CHECK_EQ(code1_1, manager.LookupCode(code1_1->instructions().start()));
}
WasmCode* code0 = AddCode(nm1.get(), 0, kCodeAlignment);
WasmCode* code1 = AddCode(nm1.get(), 1, kCodeAlignment);
CHECK_EQ(0, code0->index());
CHECK_EQ(1, code1->index());
CHECK_EQ(code1, manager.LookupCode(code1->instructions().start()));
WasmCode* code1_1 = AddCode(nm1.get(), 1, kCodeAlignment);
CHECK_EQ(1, code1_1->index());
CHECK_EQ(code1, manager.LookupCode(code1->instructions().start()));
CHECK_EQ(code1_1, manager.LookupCode(code1_1->instructions().start()));
}
} // namespace wasm_heap_unittest