1 /*
2 * Copyright (c) 2022 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/mem/heap_region_allocator.h"
17
18 #include "ecmascript/jit/jit.h"
19 #include "ecmascript/mem/mem_map_allocator.h"
20
21 namespace panda::ecmascript {
22 constexpr size_t PANDA_POOL_ALIGNMENT_IN_BYTES = 256_KB;
23
HeapRegionAllocator(JSRuntimeOptions & option)24 HeapRegionAllocator::HeapRegionAllocator(JSRuntimeOptions &option)
25 {
26 enablePageTagThreadId_ = option.EnablePageTagThreadId();
27 }
28
AllocateAlignedRegion(Space * space,size_t capacity,JSThread * thread,BaseHeap * heap,bool isFresh)29 Region *HeapRegionAllocator::AllocateAlignedRegion(Space *space, size_t capacity, JSThread* thread, BaseHeap *heap,
30 bool isFresh)
31 {
32 if (capacity == 0) { // LOCV_EXCL_BR_LINE
33 LOG_ECMA_MEM(FATAL) << "capacity must have a size bigger than 0";
34 UNREACHABLE();
35 }
36 RegionSpaceFlag flags = space->GetRegionFlag();
37 RegionTypeFlag typeFlag = isFresh ? RegionTypeFlag::FRESH : RegionTypeFlag::DEFAULT;
38 bool isRegular = (flags != RegionSpaceFlag::IN_HUGE_OBJECT_SPACE &&
39 flags != RegionSpaceFlag::IN_HUGE_MACHINE_CODE_SPACE &&
40 flags != RegionSpaceFlag::IN_SHARED_HUGE_OBJECT_SPACE);
41 bool isCompress = flags == RegionSpaceFlag::IN_NON_MOVABLE_SPACE ||
42 flags == RegionSpaceFlag::IN_SHARED_NON_MOVABLE || flags == RegionSpaceFlag::IN_READ_ONLY_SPACE ||
43 flags == RegionSpaceFlag::IN_SHARED_READ_ONLY_SPACE;
44 bool isMachineCode = (flags == RegionSpaceFlag::IN_MACHINE_CODE_SPACE ||
45 flags == RegionSpaceFlag::IN_HUGE_MACHINE_CODE_SPACE);
46 JSThread::ThreadId tid = 0;
47 bool shouldPageTag = AllocateRegionShouldPageTag(space);
48 if (enablePageTagThreadId_) {
49 tid = thread ? thread->GetThreadId() : JSThread::GetCurrentThreadId();
50 }
51 auto pool = MemMapAllocator::GetInstance()->Allocate(tid, capacity, DEFAULT_REGION_SIZE,
52 ToSpaceTypeName(space->GetSpaceType()), isRegular, isCompress, isMachineCode,
53 Jit::GetInstance()->IsEnableJitFort(), shouldPageTag);
54 void *mapMem = pool.GetMem();
55 if (mapMem == nullptr) { // LOCV_EXCL_BR_LINE
56 if (heap->InGC()) {
57 // Donot crash in GC.
58 TemporarilyEnsureAllocateionAlwaysSuccess(heap);
59 pool = MemMapAllocator::GetInstance()->Allocate(tid, capacity, DEFAULT_REGION_SIZE,
60 ToSpaceTypeName(space->GetSpaceType()), isRegular, isCompress, isMachineCode,
61 Jit::GetInstance()->IsEnableJitFort(), shouldPageTag);
62 mapMem = pool.GetMem();
63 if (mapMem == nullptr) {
64 // This should not happen
65 LOG_ECMA_MEM(FATAL) << "pool is empty in GC unexpectedly";
66 UNREACHABLE();
67 }
68 } else {
69 if (thread != nullptr && thread->GetEcmaVM()->IsInitialized()) {
70 Heap *localHeap = const_cast<Heap *>(thread->GetEcmaVM()->GetHeap());
71 localHeap->DumpHeapSnapshotBeforeOOM();
72 heap->ThrowOutOfMemoryErrorForDefault(thread, DEFAULT_REGION_SIZE,
73 "HeapRegionAllocator::AllocateAlignedRegion", false);
74 }
75 LOG_ECMA_MEM(FATAL) << "pool is empty " << annoMemoryUsage_.load(std::memory_order_relaxed);
76 UNREACHABLE();
77 }
78 }
79 #if ECMASCRIPT_ENABLE_ZAP_MEM
80 if (memset_s(mapMem, capacity, 0, capacity) != EOK) { // LOCV_EXCL_BR_LINE
81 LOG_FULL(FATAL) << "memset_s failed";
82 UNREACHABLE();
83 }
84 #endif
85 IncreaseAnnoMemoryUsage(capacity);
86
87 uintptr_t mem = ToUintPtr(mapMem);
88 // Check that the address is 256K byte aligned
89 LOG_ECMA_IF(AlignUp(mem, PANDA_POOL_ALIGNMENT_IN_BYTES) != mem, FATAL) << "region not align by 256KB";
90 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
91 uintptr_t begin = AlignUp(mem + sizeof(Region), static_cast<size_t>(MemAlignment::MEM_ALIGN_REGION));
92 uintptr_t end = mem + capacity;
93
94 Region *region = new (ToVoidPtr(mem)) Region(heap->GetNativeAreaAllocator(), mem, begin, end, flags, typeFlag);
95 region->Initialize();
96 std::atomic_thread_fence(std::memory_order_seq_cst);
97 return region;
98 }
99
FreeRegion(Region * region,size_t cachedSize,bool skipCache)100 void HeapRegionAllocator::FreeRegion(Region *region, size_t cachedSize, bool skipCache)
101 {
102 auto size = region->GetCapacity();
103 bool isRegular = !region->InHugeObjectSpace() && !region->InHugeMachineCodeSpace() &&
104 !region->InSharedHugeObjectSpace();
105 bool isCompress = region->InNonMovableSpace() || region->InSharedNonMovableSpace() ||
106 region->InReadOnlySpace() || region->InSharedReadOnlySpace();
107 auto allocateBase = region->GetAllocateBase();
108 bool shouldPageTag = FreeRegionShouldPageTag(region);
109 bool inHugeMachineCodeSpace = region->InHugeMachineCodeSpace();
110
111 DecreaseAnnoMemoryUsage(size);
112 region->Invalidate();
113 region->ClearMembers();
114
115 // Use the mmap interface to clean up the MAP_JIT tag bits on memory.
116 // The MAP_JIT flag prevents the mprotect interface from setting PROT_WRITE for memory.
117 if (inHugeMachineCodeSpace) {
118 MemMap memMap = PageMap(size, PAGE_PROT_NONE, 0, ToVoidPtr(allocateBase), PAGE_FLAG_MAP_FIXED);
119 if (memMap.GetMem() == nullptr) {
120 LOG_ECMA_MEM(FATAL) << "Failed to clear the MAP_JIT flag for the huge machine code space.";
121 }
122 }
123 #if ECMASCRIPT_ENABLE_ZAP_MEM
124 if (memset_s(ToVoidPtr(allocateBase), size, INVALID_VALUE, size) != EOK) { // LOCV_EXCL_BR_LINE
125 LOG_FULL(FATAL) << "memset_s failed";
126 UNREACHABLE();
127 }
128 #endif
129 MemMapAllocator::GetInstance()->CacheOrFree(ToVoidPtr(allocateBase), size, isRegular, isCompress, cachedSize,
130 shouldPageTag, skipCache);
131 }
132
TemporarilyEnsureAllocateionAlwaysSuccess(BaseHeap * heap)133 void HeapRegionAllocator::TemporarilyEnsureAllocateionAlwaysSuccess(BaseHeap *heap)
134 {
135 // Make MemMapAllocator infinite, and record to Dump&Fatal when GC completed.
136 heap->ShouldForceThrowOOMError();
137 MemMapAllocator::GetInstance()->TransferToInfiniteModeForGC();
138 }
139
AllocateRegionShouldPageTag(Space * space) const140 bool HeapRegionAllocator::AllocateRegionShouldPageTag(Space *space) const
141 {
142 if (enablePageTagThreadId_) {
143 return true;
144 }
145 MemSpaceType type = space->GetSpaceType();
146 // Both LocalSpace and OldSpace belong to OldSpace.
147 return type != MemSpaceType::OLD_SPACE && type != MemSpaceType::LOCAL_SPACE;
148 }
149
FreeRegionShouldPageTag(Region * region) const150 bool HeapRegionAllocator::FreeRegionShouldPageTag(Region *region) const
151 {
152 if (enablePageTagThreadId_) {
153 return true;
154 }
155 // There is no LocalSpace tag in region.
156 return !region->InOldSpace();
157 }
158 } // namespace panda::ecmascript
159