/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include "jit/BaselineJIT.h" #include "jit/JitCode.h" #include "jit/JitcodeMap.h" #include "jit/JitRuntime.h" #include "js/GCAPI.h" #include "js/ProfilingFrameIterator.h" #include "js/ProfilingStack.h" #include "jsapi-tests/tests.h" #include "vm/JSContext.h" #include "vm/JSScript-inl.h" BEGIN_TEST(testJitcodeGlobalTable_sampledAddressAfterCodeReuse) { if (!js::jit::IsBaselineJitEnabled(cx)) { return true; } JS::Rooted code( cx, js::jit::JitCode::FromExecutable( cx->runtime()->jitRuntime()->baselineInterpreter().codeRaw())); js::jit::JitcodeGlobalTable table; uint8_t addresses[32]; void* sampledAddr = addresses + 12; // The ranges are synthetic: no code is executed from them. Clearing the // JitCode pointer simulates collection before another entry reuses the range. auto first = js::jit::MakeJitcodeGlobalEntry( cx, code, addresses + 8, addresses + 16); CHECK(first); auto* firstEntry = first.get(); CHECK(table.addEntry(std::move(first))); CHECK(table.lookupForSampler(sampledAddr, 1) == firstEntry); CHECK(table.lookupForSampler(sampledAddr, 3) == firstEntry); *firstEntry->jitcodePtr() = nullptr; auto second = js::jit::MakeJitcodeGlobalEntry( cx, code, addresses + 8, addresses + 16); CHECK(second); auto* secondEntry = second.get(); CHECK(table.addEntry(std::move(second))); CHECK(checkLookup(table, sampledAddr, 1, firstEntry, 3)); CHECK(checkLookup(table, sampledAddr, 4, secondEntry, UINT64_MAX)); CHECK(table.lookupForSampler(sampledAddr, 5) == secondEntry); CHECK(table.lookupForSampler(sampledAddr, 7) == secondEntry); CHECK(checkLookup(table, sampledAddr, 3, firstEntry, 3)); CHECK(checkLookup(table, sampledAddr, 5, secondEntry, UINT64_MAX)); *secondEntry->jitcodePtr() = nullptr; auto third = js::jit::MakeJitcodeGlobalEntry( cx, code, addresses + 10, addresses + 24); CHECK(third); auto* thirdEntry = third.get(); CHECK(table.addEntry(std::move(third))); CHECK(table.lookupForSampler(sampledAddr, 9) == thirdEntry); CHECK(checkLookup(table, sampledAddr, 3, firstEntry, 3)); CHECK(checkLookup(table, sampledAddr, 5, secondEntry, 7)); CHECK(checkLookup(table, sampledAddr, 7, secondEntry, 7)); CHECK(checkLookup(table, sampledAddr, 9, thirdEntry, UINT64_MAX)); CHECK(checkLookup(table, addresses + 8, 3, firstEntry, 3)); return true; } bool checkLookup(js::jit::JitcodeGlobalTable& table, void* addr, uint64_t samplePos, js::jit::JitcodeGlobalEntry* expected, uint64_t expectedLastSamplePos) { uint64_t lastSamplePos; CHECK(table.lookupForSamplePosition(addr, samplePos, &lastSamplePos) == expected); CHECK_EQUAL(lastSamplePos, expectedLastSamplePos); return true; } END_TEST(testJitcodeGlobalTable_sampledAddressAfterCodeReuse) BEGIN_TEST(testJitcodeGlobalTable_sampledAddressAfterGC) { if (!js::jit::IsBaselineJitEnabled(cx)) { return true; } js::SetContextProfilingStack(cx, &profilingStack); js::EnableContextProfilingStack(cx, true); CHECK(cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled( cx->runtime())); EXEC( "function victim(x) { return x + 1; }" "for (var i = 0; i < 200; i++) victim(i);"); JS::RootedValue function(cx); EVAL("victim", &function); JS::Rooted script( cx, function.toObject().as().nonLazyScript()); CHECK(script->hasBaselineScript()); js::jit::JitCode* code = script->baselineScript()->method(); void* sampledAddr = code->raw() + (code->rawEnd() - code->raw()) / 2; js::jit::JitcodeGlobalTable* table = cx->runtime()->jitRuntime()->getJitcodeGlobalTable(); // The buffer range start stays at 0, so this sample is never evicted. CHECK(table->lookupForSampler(sampledAddr, 1)); CHECK(resolvesToVictim(sampledAddr)); JS::PrepareForFullGC(cx); JS::NonIncrementalGC(cx, JS::GCOptions::Shrink, JS::GCReason::API); CHECK(!script->hasBaselineScript()); CHECK(resolvesToVictim(sampledAddr)); return true; } ProfilingStack profilingStack; bool resolvesToVictim(void* addr) { uint32_t frameCount = 0; for (JS::ProfiledFrameHandle handle : JS::GetProfiledFrames(cx, addr, 1)) { CHECK(handle.label()); CHECK(strncmp(handle.label(), "victim ", 7) == 0); frameCount++; } CHECK_EQUAL(frameCount, 1u); return true; } void uninit() override { js::EnableContextProfilingStack(cx, false); js::SetContextProfilingStack(cx, nullptr); RuntimeTest::uninit(); } END_TEST(testJitcodeGlobalTable_sampledAddressAfterGC)