* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/ecma_vm.h"
#include "ecmascript/js_tagged_value.h"
#include "ecmascript/tests/test_helper.h"
#include "ecmascript/mem/concurrent_marker.h"
#include "ecmascript/mem/partial_gc.h"
#include "ecmascript/mem/jit_fort_memdesc.h"
#include "ecmascript/mem/jit_fort.h"
#include "ecmascript/mem/machine_code.h"
using namespace panda;
using namespace panda::ecmascript;
namespace panda::test {
class JitFortTest : public BaseTestWithScope<false> {
public:
void SetUp() override
{
JSRuntimeOptions options;
instance = JSNApi::CreateEcmaVM(options);
ASSERT_TRUE(instance != nullptr) << "Cannot create EcmaVM";
thread = instance->GetJSThread();
thread->ManagedCodeBegin();
scope = new EcmaHandleScope(thread);
auto heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
heap->GetConcurrentMarker()->EnableConcurrentMarking(EnableConcurrentMarkType::ENABLE);
heap->GetSweeper()->EnableConcurrentSweep(EnableConcurrentSweepType::ENABLE);
}
};
HWTEST_F_L0(JitFortTest, AddRegionTest001)
{
JitFort *jitFort = new JitFort();
bool res = jitFort->AddRegion();
ASSERT_EQ(res, true);
}
HWTEST_F_L0(JitFortTest, AddRegionTest002)
{
JitFort *jitFort = new JitFort();
for (size_t i = 0; i < JitFort::MAX_JIT_FORT_REGIONS; i++) {
jitFort->AddRegion();
}
ASSERT_EQ(jitFort->AddRegion(), false);
}
HWTEST_F_L0(JitFortTest, AllocateTest001)
{
MachineCodeDesc desc;
desc.instructionsSize = 18;
JitFort *jitFort = new JitFort();
ASSERT_NE(jitFort, nullptr);
jitFort->Allocate(&desc);
}
HWTEST_F_L0(JitFortTest, AllocateTest002)
{
MachineCodeDesc desc;
desc.instructionsSize = 18;
desc.isHugeObj = false;
JitFort *jitFort = new JitFort();
ASSERT_NE(jitFort, nullptr);
uintptr_t retAddr = jitFort->Allocate(&desc);
bool inRange = jitFort->InSmallRange(retAddr);
ASSERT_EQ(inRange, true);
}
HWTEST_F_L0(JitFortTest, AllocateTest003)
{
MachineCodeDesc desc;
desc.instructionsSize = 18;
desc.isHugeObj = true;
JitFort *jitFort = new JitFort();
ASSERT_NE(jitFort, nullptr);
uintptr_t retAddr = jitFort->Allocate(&desc);
bool inHugeRange = jitFort->InHugeRange(retAddr);
ASSERT_EQ(inHugeRange, true);
}
HWTEST_F_L0(JitFortTest, GetDescTest001)
{
MemDescPool *pool = new MemDescPool(1, 1);
ASSERT_NE(pool, nullptr);
pool->GetDescFromPool();
}
HWTEST_F_L0(JitFortTest, MemDescPoolFreeTest001)
{
MemDescPool *pool = new MemDescPool(1, 1);
ASSERT_NE(pool, nullptr);
pool->~MemDescPool();
}
HWTEST_F_L0(JitFortTest, InitRegionTest001)
{
JitFort *jitFort = new JitFort();
ASSERT_NE(jitFort, nullptr);
jitFort->InitRegions();
}
HWTEST_F_L0(JitFortTest, InRangeTest001)
{
JitFort *jitFort = new JitFort();
bool result = jitFort->InSmallRange(1);
ASSERT_EQ(result, false);
}
* @tc.name: SingletonInstanceTest001
* @tc.desc: Test singleton-instance consistency and basic functionality
*/
HWTEST_F_L0(JitFortTest, SingletonInstanceTest001)
{
JitFort *jitFort1 = new JitFort();
JitFort *jitFort2 = new JitFort();
ASSERT_NE(jitFort1, nullptr);
ASSERT_NE(jitFort2, nullptr);
MachineCodeDesc desc1, desc2;
desc1.instructionsSize = 64;
desc1.isHugeObj = false;
desc2.instructionsSize = 128;
desc2.isHugeObj = false;
uintptr_t addr1 = jitFort1->Allocate(&desc1);
uintptr_t addr2 = jitFort2->Allocate(&desc2);
ASSERT_NE(addr1, ToUintPtr(nullptr));
ASSERT_NE(addr2, ToUintPtr(nullptr));
EXPECT_TRUE(jitFort1->InSmallRange(addr1));
EXPECT_TRUE(jitFort2->InSmallRange(addr2));
JitFortRegion *region1 = jitFort1->ObjectAddressToRange(addr1);
JitFortRegion *region2 = jitFort2->ObjectAddressToRange(addr2);
ASSERT_NE(region1, nullptr);
ASSERT_NE(region2, nullptr);
delete jitFort1;
delete jitFort2;
}
* @tc.name: HugeRegionBasicTest002
* @tc.desc: Test huge region allocation and basic operations
*/
HWTEST_F_L0(JitFortTest, HugeRegionBasicTest002)
{
JitFort *jitFort = new JitFort();
MachineCodeDesc desc;
desc.instructionsSize = 512 * 1024;
desc.isHugeObj = true;
uintptr_t addr = jitFort->Allocate(&desc);
ASSERT_NE(addr, ToUintPtr(nullptr));
EXPECT_TRUE(jitFort->InHugeRange(addr));
EXPECT_TRUE(jitFort->InJitFortRange(addr));
EXPECT_FALSE(jitFort->InSmallRange(addr));
JitFortRegion *region = jitFort->ObjectAddressToRange(addr);
ASSERT_NE(region, nullptr);
delete jitFort;
}
* @tc.name: HugeRegionSizeVarietyTest003
* @tc.desc: Test huge region allocation with various sizes
*/
HWTEST_F_L0(JitFortTest, HugeRegionSizeVarietyTest003)
{
JitFort *jitFort = new JitFort();
std::vector<uintptr_t> addresses;
const size_t sizes[] = {
256 * 1024,
512 * 1024,
1024 * 1024,
2048 * 1024
};
for (size_t size : sizes) {
MachineCodeDesc desc;
desc.instructionsSize = size;
desc.isHugeObj = true;
uintptr_t addr = jitFort->Allocate(&desc);
if (addr != ToUintPtr(nullptr)) {
addresses.push_back(addr);
EXPECT_TRUE(jitFort->InHugeRange(addr)) << "Size " << size << " address: " << std::hex << addr;
EXPECT_FALSE(jitFort->InSmallRange(addr)) << "Size " << size << " should not be in small range";
}
}
EXPECT_GT(addresses.size(), 0) << "Should have at least one successful allocation";
EXPECT_LE(addresses.size(), 4) << "Cannot allocate more than 4 huge objects in test";
for (uintptr_t addr : addresses) {
EXPECT_TRUE(jitFort->InHugeRange(addr));
EXPECT_FALSE(jitFort->InSmallRange(addr));
}
delete jitFort;
}
* @tc.name: MixedAllocationScenariosTest004
* @tc.desc: Test mixed small and huge object allocation scenarios
*/
HWTEST_F_L0(JitFortTest, MixedAllocationScenariosTest004)
{
JitFort *jitFort = new JitFort();
std::vector<uintptr_t> smallAddrs, hugeAddrs;
for (int i = 0; i < 15; i++) {
MachineCodeDesc desc;
if (i % 3 == 2) {
desc.instructionsSize = 512 * 1024;
desc.isHugeObj = true;
uintptr_t addr = jitFort->Allocate(&desc);
if (addr != ToUintPtr(nullptr)) {
hugeAddrs.push_back(addr);
EXPECT_TRUE(jitFort->InHugeRange(addr));
EXPECT_FALSE(jitFort->InSmallRange(addr));
}
} else {
desc.instructionsSize = 64 + (i * 32);
desc.isHugeObj = false;
uintptr_t addr = jitFort->Allocate(&desc);
if (addr != ToUintPtr(nullptr)) {
smallAddrs.push_back(addr);
EXPECT_TRUE(jitFort->InSmallRange(addr));
EXPECT_FALSE(jitFort->InHugeRange(addr));
}
}
}
EXPECT_GT(smallAddrs.size(), 0) << "Should have successful small allocations";
EXPECT_GT(hugeAddrs.size(), 0) << "Should have successful huge allocations";
for (uintptr_t smallAddr : smallAddrs) {
for (uintptr_t hugeAddr : hugeAddrs) {
EXPECT_NE(smallAddr, hugeAddr) << "Small and huge addresses should not overlap";
}
}
delete jitFort;
}
* @tc.name: MemoryManagementOperationsTest005
* @tc.desc: Test memory management operations
*/
HWTEST_F_L0(JitFortTest, MemoryManagementOperationsTest005)
{
JitFort *jitFort = new JitFort();
MachineCodeDesc smallDesc;
smallDesc.instructionsSize = 256;
smallDesc.isHugeObj = false;
uintptr_t smallAddr = jitFort->Allocate(&smallDesc);
ASSERT_NE(smallAddr, ToUintPtr(nullptr));
jitFort->MarkJitFortMemAwaitInstall(smallAddr, smallDesc.instructionsSize, false);
MachineCodeDesc hugeDesc;
hugeDesc.instructionsSize = 256 * 1024;
hugeDesc.isHugeObj = true;
uintptr_t hugeAddr = jitFort->Allocate(&hugeDesc);
ASSERT_NE(hugeAddr, ToUintPtr(nullptr));
jitFort->MarkJitFortMemAwaitInstall(hugeAddr, hugeDesc.instructionsSize, true);
JitFortRegion *smallRegion = jitFort->ObjectAddressToRange(smallAddr);
JitFortRegion *hugeRegion = jitFort->ObjectAddressToRange(hugeAddr);
ASSERT_NE(smallRegion, nullptr);
ASSERT_NE(hugeRegion, nullptr);
EXPECT_NE(smallRegion, hugeRegion) << "Small and huge objects should be in different regions";
jitFort->Sweep(false);
jitFort->Sweep(true);
jitFort->AsyncSweep(false);
jitFort->AsyncSweep(true);
delete jitFort;
}
* @tc.name: AddressRangeValidationTest006
* @tc.desc: Test address range validation methods
*/
HWTEST_F_L0(JitFortTest, AddressRangeValidationTest006)
{
JitFort *jitFort = new JitFort();
MachineCodeDesc smallDesc, hugeDesc;
smallDesc.instructionsSize = 128;
smallDesc.isHugeObj = false;
hugeDesc.instructionsSize = 256 * 1024;
hugeDesc.isHugeObj = true;
uintptr_t smallAddr = jitFort->Allocate(&smallDesc);
uintptr_t hugeAddr = jitFort->Allocate(&hugeDesc);
ASSERT_NE(smallAddr, ToUintPtr(nullptr));
ASSERT_NE(hugeAddr, ToUintPtr(nullptr));
EXPECT_TRUE(jitFort->InSmallRange(smallAddr));
EXPECT_TRUE(jitFort->InHugeRange(hugeAddr));
EXPECT_TRUE(jitFort->InJitFortRange(smallAddr));
EXPECT_TRUE(jitFort->InJitFortRange(hugeAddr));
EXPECT_FALSE(jitFort->InSmallRange(0));
EXPECT_FALSE(jitFort->InHugeRange(0));
EXPECT_FALSE(jitFort->InJitFortRange(0));
if (smallAddr > 1) {
EXPECT_FALSE(jitFort->InSmallRange(smallAddr - 1));
EXPECT_TRUE(jitFort->InSmallRange(smallAddr + 1));
}
delete jitFort;
}
* @tc.name: RegionManagementTest007
* @tc.desc: Test region management operations
*/
HWTEST_F_L0(JitFortTest, RegionManagementTest007)
{
JitFort *jitFort = new JitFort();
JitFortRegion *regionList = jitFort->GetRegionList();
EXPECT_NE(regionList, nullptr) << "Region list should be initialized";
bool addResult = jitFort->AddRegion();
EXPECT_TRUE(addResult) << "Should be able to add additional regions";
JitFortRegion *updatedRegionList = jitFort->GetRegionList();
EXPECT_NE(updatedRegionList, nullptr);
std::vector<JitFortRegion *> regions;
MachineCodeDesc desc;
desc.instructionsSize = 64;
desc.isHugeObj = false;
for (int i = 0; i < 10; i++) {
uintptr_t addr = jitFort->Allocate(&desc);
if (addr != ToUintPtr(nullptr)) {
JitFortRegion *region = jitFort->ObjectAddressToRange(addr);
ASSERT_NE(region, nullptr);
regions.push_back(region);
jitFort->MarkJitFortMemAwaitInstall(addr, desc.instructionsSize, false);
}
}
EXPECT_GT(regions.size(), 0) << "Should have allocations in regions";
delete jitFort;
}
* @tc.name: FortSizeCalculationTest008
* @tc.desc: Test fort size calculation methods
*/
HWTEST_F_L0(JitFortTest, FortSizeCalculationTest008)
{
JitFort *jitFort = new JitFort();
size_t smallSize = 64;
size_t mediumSize = 512;
size_t largeSize = 2048;
size_t hugeSize = 1024 * 1024;
size_t allocSmall = jitFort->FortAllocSize(smallSize);
size_t allocMedium = jitFort->FortAllocSize(mediumSize);
size_t allocLarge = jitFort->FortAllocSize(largeSize);
size_t allocHuge = jitFort->FortAllocSize(hugeSize);
EXPECT_EQ(allocSmall % jitFort->FORT_BUF_ALIGN, 0);
EXPECT_EQ(allocMedium % jitFort->FORT_BUF_ALIGN, 0);
EXPECT_EQ(allocLarge % jitFort->FORT_BUF_ALIGN, 0);
EXPECT_EQ(allocHuge % jitFort->FORT_BUF_ALIGN, 0);
EXPECT_GE(allocSmall, smallSize);
EXPECT_GE(allocMedium, mediumSize);
EXPECT_GE(allocLarge, largeSize);
EXPECT_GE(allocHuge, hugeSize);
delete jitFort;
}
* @tc.name: ConcurrentThreadSafetyTest009
* @tc.desc: Test concurrent access thread safety
*/
HWTEST_F_L0(JitFortTest, ConcurrentThreadSafetyTest009)
{
const int threadCount = 3;
const int opsPerThread = 25;
std::atomic<int> successCount {0};
std::vector<std::thread> threads;
auto workerThread = [&successCount]() {
for (int i = 0; i < opsPerThread; i++) {
JitFort *jitFort = new JitFort();
MachineCodeDesc desc;
desc.instructionsSize = 128 + (rand() % 1024);
desc.isHugeObj = (rand() % 4) == 0;
uintptr_t addr = jitFort->Allocate(&desc);
if (addr == ToUintPtr(nullptr)) {
delete jitFort;
continue;
}
successCount++;
if (desc.isHugeObj) {
EXPECT_TRUE(jitFort->InHugeRange(addr));
EXPECT_FALSE(jitFort->InSmallRange(addr));
} else {
EXPECT_TRUE(jitFort->InSmallRange(addr));
EXPECT_FALSE(jitFort->InHugeRange(addr));
}
delete jitFort;
}
};
for (int i = 0; i < threadCount; i++) {
threads.emplace_back(workerThread);
}
for (auto &thread : threads) {
thread.join();
}
EXPECT_GT(successCount.load(), 0) << "Should have successful concurrent operations";
}
* @tc.name: ResourceAvailabilityTest010
* @tc.desc: Test resource availability methods
*/
HWTEST_F_L0(JitFortTest, ResourceAvailabilityTest010)
{
bool resourceAvailable = JitFort::IsResourceAvailable();
EXPECT_TRUE(resourceAvailable) << "JitFort resources should be available";
JitFort *jitFort = new JitFort();
EXPECT_TRUE(JitFort::IsResourceAvailable()) << "Resource availability should remain true after instance creation";
MachineCodeDesc descSmall, descLarge;
descSmall.instructionsSize = 64;
descSmall.isHugeObj = false;
descLarge.instructionsSize = 1024 * 1024;
descLarge.isHugeObj = true;
uintptr_t smallAddr = jitFort->Allocate(&descSmall);
if (smallAddr != ToUintPtr(nullptr)) {
EXPECT_TRUE(jitFort->InSmallRange(smallAddr));
}
uintptr_t largeAddr = jitFort->Allocate(&descLarge);
if (largeAddr != ToUintPtr(nullptr)) {
EXPECT_TRUE(jitFort->InHugeRange(largeAddr));
}
delete jitFort;
EXPECT_TRUE(JitFort::IsResourceAvailable())
<< "Resource availability should not be affected by instance destruction";
}
* @tc.name: AddrToFortRegionIndexTest011
* @tc.desc: Test address to region index calculation
*/
HWTEST_F_L0(JitFortTest, AddrToFortRegionIndexTest011)
{
JitFort *jitFort = new JitFort();
MachineCodeDesc desc;
desc.instructionsSize = 256;
desc.isHugeObj = false;
uintptr_t addr = jitFort->Allocate(&desc);
ASSERT_NE(addr, ToUintPtr(nullptr));
uint32_t regionIndex = jitFort->AddrToFortRegionIdx(addr);
EXPECT_LT(regionIndex, JitFort::MAX_JIT_FORT_REGIONS);
EXPECT_GE(regionIndex, 0);
delete jitFort;
}
* @tc.name: MemoryLeakTest012
* @tc.desc: Test for memory leaks during repeated allocation/deallocation
*/
HWTEST_F_L0(JitFortTest, MemoryLeakTest012)
{
const int cycles = 50;
const int perCycle = 10;
for (int cycle = 0; cycle < cycles; cycle++) {
JitFort *jitFort = new JitFort();
std::vector<uintptr_t> addresses;
for (int i = 0; i < perCycle; i++) {
MachineCodeDesc desc;
desc.instructionsSize = 64 + (rand() % 1024);
desc.isHugeObj = (rand() % 3) == 0;
uintptr_t addr = jitFort->Allocate(&desc);
if (addr != ToUintPtr(nullptr)) {
addresses.push_back(addr);
jitFort->MarkJitFortMemAwaitInstall(addr, desc.instructionsSize, desc.isHugeObj);
}
}
delete jitFort;
}
}
}