1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/ecma_vm.h"
17#include "ecmascript/global_env.h"
18#include "ecmascript/js_handle.h"
19#include "ecmascript/js_runtime_options.h"
20#include "ecmascript/log.h"
21#include "ecmascript/mem/concurrent_marker.h"
22#include "ecmascript/mem/space.h"
23#include "ecmascript/mem/verification.h"
24#include "ecmascript/object_factory.h"
25#include "ecmascript/tagged_array-inl.h"
26#include "ecmascript/tests/test_helper.h"
27
28#include <csetjmp>
29#include <csignal>
30#include <sys/syscall.h>
31using namespace panda::ecmascript;
32
33namespace panda::test {
34class ReadOnlySpaceTest : public BaseTestWithScope<false> {
35public:
36    void SetUp() override
37    {
38        InitializeLogger();
39        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
40        factory = thread->GetEcmaVM()->GetFactory();
41        const_cast<Heap *>(thread->GetEcmaVM()->GetHeap())->SetMarkType(MarkType::MARK_FULL);
42    }
43
44    void InitializeLogger()
45    {
46        panda::ecmascript::JSRuntimeOptions runtimeOptions;
47        runtimeOptions.SetLogLevel("error");
48        ecmascript::Log::Initialize(runtimeOptions);
49    }
50
51    ObjectFactory *factory {nullptr};
52};
53
54static sigjmp_buf g_env;
55static bool g_segmentfault_flag = false;
56class ReadOnlyTestManager {
57public:
58    // static constexpr int RO_SEGMENTFAULT = 1;
59    static void ProcessReadOnlySegmentFault(int sig)
60    {
61        g_segmentfault_flag = true;
62        siglongjmp(g_env, sig);
63    }
64
65    static int RegisterSignal()
66    {
67        struct sigaction act;
68        act.sa_handler = ProcessReadOnlySegmentFault;
69        sigemptyset(&act.sa_mask);
70        sigaddset(&act.sa_mask, SIGQUIT);
71        act.sa_flags = SA_RESETHAND;
72        return sigaction(SIGSEGV, &act, nullptr);
73    }
74};
75
76static pid_t ForkBySyscall(void)
77{
78#ifdef SYS_fork
79    return syscall(SYS_fork);
80#else
81    return syscall(SYS_clone, SIGCHLD, 0);
82#endif
83}
84
85HWTEST_F_L0(ReadOnlySpaceTest, ReadOnlyTest)
86{
87    auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
88    heap->GetReadOnlySpace()->SetReadOnly();
89    if (ReadOnlyTestManager::RegisterSignal() == -1) {
90        perror("sigaction error");
91        exit(1);
92    }
93    SharedHeap::GetInstance()->WaitGCFinished(thread);
94    auto ret = sigsetjmp(g_env, 1);
95    if (ret != SIGSEGV) {
96        heap->AllocateReadOnlyOrHugeObject(
97            JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
98    } else {
99        // catch signal SIGSEGV caused by modify read only memory
100        EXPECT_TRUE(g_segmentfault_flag);
101    }
102}
103
104HWTEST_F_L0(ReadOnlySpaceTest, AllocateTest)
105{
106    auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
107    auto *object = heap->AllocateReadOnlyOrHugeObject(
108        JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
109    auto *region = Region::ObjectAddressToRange(object);
110    EXPECT_TRUE(region->InReadOnlySpace());
111}
112
113HWTEST_F_L0(ReadOnlySpaceTest, CompactHeapBeforeForkTest)
114{
115    auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
116    std::string rawStr = "test string";
117    JSHandle<EcmaString> string = factory->NewFromStdString(rawStr);
118    JSHandle<JSObject> obj = factory->NewEmptyJSObject();
119    auto *regionBefore = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
120    auto *objRegionBefore = Region::ObjectAddressToRange(obj.GetObject<TaggedObject>());
121    EXPECT_TRUE(regionBefore->InSharedHeap());
122    EXPECT_FALSE(objRegionBefore->InReadOnlySpace());
123    heap->CompactHeapBeforeFork();
124    auto *regionAfter = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
125    auto *objRegionAfter = Region::ObjectAddressToRange(obj.GetObject<TaggedObject>());
126    EXPECT_TRUE(regionAfter->InSharedHeap());
127    EXPECT_FALSE(objRegionAfter->InReadOnlySpace());
128}
129
130HWTEST_F_L0(ReadOnlySpaceTest, GCTest)
131{
132    auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
133    auto *object = heap->AllocateReadOnlyOrHugeObject(
134        JSHClass::Cast(thread->GlobalConstants()->GetBigIntClass().GetTaggedObject()));
135    heap->CollectGarbage(TriggerGCType::YOUNG_GC);
136    heap->CollectGarbage(TriggerGCType::OLD_GC);
137    heap->CollectGarbage(TriggerGCType::FULL_GC);
138    auto *region = Region::ObjectAddressToRange(object);
139    EXPECT_TRUE(region->InReadOnlySpace());
140}
141
142HWTEST_F_L0(ReadOnlySpaceTest, ForkTest)
143{
144    auto vm = thread->GetEcmaVM();
145    auto *heap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
146    std::string rawStr = "fork string";
147    JSHandle<EcmaString> string = factory->NewFromStdString(rawStr);
148    JSNApi::PreFork(vm);
149    if (ForkBySyscall() != 0) {
150        // test gc in parent process
151        heap->CollectGarbage(TriggerGCType::OLD_GC);
152    } else {
153        panda::RuntimeOption postOption;
154        JSNApi::PostFork(vm, postOption);
155        // test gc in child process
156        heap->CollectGarbage(TriggerGCType::OLD_GC);
157        auto *region = Region::ObjectAddressToRange(string.GetObject<TaggedObject>());
158        EXPECT_TRUE(region->InSharedHeap());
159    }
160}
161}  // namespace panda::test
162