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/js_thread.h"
17 #include "ecmascript/log_wrapper.h"
18 #include "ecmascript/platform/file.h"
19 #if defined(ENABLE_EXCEPTION_BACKTRACE)
20 #include "ecmascript/platform/backtrace.h"
21 #endif
22 #include "ecmascript/ecma_global_storage.h"
23 #include "ecmascript/ecma_param_configuration.h"
24 #include "ecmascript/global_env_constants-inl.h"
25 #include "ecmascript/ic/properties_cache.h"
26 #include "ecmascript/interpreter/interpreter-inl.h"
27 #include "ecmascript/mem/mark_word.h"
28 #include "ecmascript/stackmap/llvm_stackmap_parser.h"
29
30 namespace panda::ecmascript {
31 using CommonStubCSigns = panda::ecmascript::kungfu::CommonStubCSigns;
32 using BytecodeStubCSigns = panda::ecmascript::kungfu::BytecodeStubCSigns;
33
34 // static
Create(EcmaVM * vm)35 JSThread *JSThread::Create(EcmaVM *vm)
36 {
37 auto jsThread = new JSThread(vm);
38 AsmInterParsedOption asmInterOpt = vm->GetJSOptions().GetAsmInterParsedOption();
39 if (asmInterOpt.enableAsm) {
40 jsThread->EnableAsmInterpreter();
41 }
42
43 jsThread->nativeAreaAllocator_ = vm->GetNativeAreaAllocator();
44 jsThread->heapRegionAllocator_ = vm->GetHeapRegionAllocator();
45 // algin with 16
46 size_t maxStackSize = vm->GetEcmaParamConfiguration().GetMaxStackSize();
47 jsThread->glueData_.frameBase_ = static_cast<JSTaggedType *>(
48 vm->GetNativeAreaAllocator()->Allocate(sizeof(JSTaggedType) * maxStackSize));
49 jsThread->glueData_.currentFrame_ = jsThread->glueData_.frameBase_ + maxStackSize;
50 EcmaInterpreter::InitStackFrame(jsThread);
51
52 if (jsThread->IsAsmInterpreter()) {
53 jsThread->glueData_.stackLimit_ = GetAsmStackLimit();
54 jsThread->glueData_.stackStart_ = GetCurrentStackPosition();
55 }
56 return jsThread;
57 }
58
JSThread(EcmaVM * vm)59 JSThread::JSThread(EcmaVM *vm) : id_(os::thread::GetCurrentThreadId()), vm_(vm)
60 {
61 auto chunk = vm->GetChunk();
62 if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
63 globalStorage_ = chunk->New<EcmaGlobalStorage<Node>>(this, vm->GetNativeAreaAllocator());
64 newGlobalHandle_ = std::bind(&EcmaGlobalStorage<Node>::NewGlobalHandle, globalStorage_, std::placeholders::_1);
65 disposeGlobalHandle_ = std::bind(&EcmaGlobalStorage<Node>::DisposeGlobalHandle, globalStorage_,
66 std::placeholders::_1);
67 setWeak_ = std::bind(&EcmaGlobalStorage<Node>::SetWeak, globalStorage_, std::placeholders::_1,
68 std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
69 clearWeak_ = std::bind(&EcmaGlobalStorage<Node>::ClearWeak, globalStorage_, std::placeholders::_1);
70 isWeak_ = std::bind(&EcmaGlobalStorage<Node>::IsWeak, globalStorage_, std::placeholders::_1);
71 } else {
72 globalDebugStorage_ =
73 chunk->New<EcmaGlobalStorage<DebugNode>>(this, vm->GetNativeAreaAllocator());
74 newGlobalHandle_ = std::bind(&EcmaGlobalStorage<DebugNode>::NewGlobalHandle, globalDebugStorage_,
75 std::placeholders::_1);
76 disposeGlobalHandle_ = std::bind(&EcmaGlobalStorage<DebugNode>::DisposeGlobalHandle, globalDebugStorage_,
77 std::placeholders::_1);
78 setWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::SetWeak, globalDebugStorage_, std::placeholders::_1,
79 std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
80 clearWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::ClearWeak, globalDebugStorage_, std::placeholders::_1);
81 isWeak_ = std::bind(&EcmaGlobalStorage<DebugNode>::IsWeak, globalDebugStorage_, std::placeholders::_1);
82 }
83 propertiesCache_ = new PropertiesCache();
84 vmThreadControl_ = new VmThreadControl();
85 }
86
~JSThread()87 JSThread::~JSThread()
88 {
89 for (auto n : handleStorageNodes_) {
90 delete n;
91 }
92 handleStorageNodes_.clear();
93 currentHandleStorageIndex_ = -1;
94 handleScopeCount_ = 0;
95 handleScopeStorageNext_ = handleScopeStorageEnd_ = nullptr;
96 if (globalStorage_ != nullptr) {
97 GetEcmaVM()->GetChunk()->Delete(globalStorage_);
98 globalStorage_ = nullptr;
99 }
100 if (globalDebugStorage_) {
101 GetEcmaVM()->GetChunk()->Delete(globalDebugStorage_);
102 globalDebugStorage_ = nullptr;
103 }
104
105 GetNativeAreaAllocator()->Free(glueData_.frameBase_, sizeof(JSTaggedType) *
106 vm_->GetEcmaParamConfiguration().GetMaxStackSize());
107 glueData_.frameBase_ = nullptr;
108 nativeAreaAllocator_ = nullptr;
109 heapRegionAllocator_ = nullptr;
110 if (propertiesCache_ != nullptr) {
111 delete propertiesCache_;
112 propertiesCache_ = nullptr;
113 }
114 if (vmThreadControl_ != nullptr) {
115 delete vmThreadControl_;
116 vmThreadControl_ = nullptr;
117 }
118 }
119
SetException(JSTaggedValue exception)120 void JSThread::SetException(JSTaggedValue exception)
121 {
122 glueData_.exception_ = exception;
123 #if defined(ENABLE_EXCEPTION_BACKTRACE)
124 if (vm_->GetJSOptions().EnableExceptionBacktrace()) {
125 LOG_ECMA(INFO) << "SetException:" << exception.GetRawData();
126 std::ostringstream stack;
127 Backtrace(stack);
128 LOG_ECMA(INFO) << stack.str();
129 }
130 #endif
131 }
132
ClearException()133 void JSThread::ClearException()
134 {
135 glueData_.exception_ = JSTaggedValue::Hole();
136 }
137
GetCurrentLexenv() const138 JSTaggedValue JSThread::GetCurrentLexenv() const
139 {
140 FrameHandler frameHandler(this);
141 return frameHandler.GetEnv();
142 }
143
GetCurrentFrame() const144 const JSTaggedType *JSThread::GetCurrentFrame() const
145 {
146 if (IsAsmInterpreter()) {
147 return GetLastLeaveFrame();
148 }
149 return GetCurrentSPFrame();
150 }
151
SetCurrentFrame(JSTaggedType * sp)152 void JSThread::SetCurrentFrame(JSTaggedType *sp)
153 {
154 if (IsAsmInterpreter()) {
155 return SetLastLeaveFrame(sp);
156 }
157 return SetCurrentSPFrame(sp);
158 }
159
GetCurrentInterpretedFrame() const160 const JSTaggedType *JSThread::GetCurrentInterpretedFrame() const
161 {
162 if (IsAsmInterpreter()) {
163 auto frameHandler = FrameHandler(this);
164 return frameHandler.GetSp();
165 }
166 return GetCurrentSPFrame();
167 }
168
IsStartGlobalLeakCheck() const169 bool JSThread::IsStartGlobalLeakCheck() const
170 {
171 return GetEcmaVM()->GetJSOptions().IsStartGlobalLeakCheck();
172 }
173
EnableGlobalObjectLeakCheck() const174 bool JSThread::EnableGlobalObjectLeakCheck() const
175 {
176 return GetEcmaVM()->GetJSOptions().EnableGlobalObjectLeakCheck();
177 }
178
EnableGlobalPrimitiveLeakCheck() const179 bool JSThread::EnableGlobalPrimitiveLeakCheck() const
180 {
181 return GetEcmaVM()->GetJSOptions().EnableGlobalPrimitiveLeakCheck();
182 }
183
WriteToStackTraceFd(std::ostringstream & buffer) const184 void JSThread::WriteToStackTraceFd(std::ostringstream &buffer) const
185 {
186 if (stackTraceFd_ < 0) {
187 return;
188 }
189 buffer << std::endl;
190 DPrintf(reinterpret_cast<fd_t>(stackTraceFd_), buffer.str());
191 buffer.str("");
192 }
193
SetStackTraceFd(int32_t fd)194 void JSThread::SetStackTraceFd(int32_t fd)
195 {
196 stackTraceFd_ = fd;
197 }
198
CloseStackTraceFd()199 void JSThread::CloseStackTraceFd()
200 {
201 if (stackTraceFd_ != -1) {
202 FSync(reinterpret_cast<fd_t>(stackTraceFd_));
203 Close(reinterpret_cast<fd_t>(stackTraceFd_));
204 stackTraceFd_ = -1;
205 }
206 }
207
Iterate(const RootVisitor & visitor,const RootRangeVisitor & rangeVisitor,const RootBaseAndDerivedVisitor & derivedVisitor)208 void JSThread::Iterate(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor,
209 const RootBaseAndDerivedVisitor &derivedVisitor)
210 {
211 if (propertiesCache_ != nullptr) {
212 propertiesCache_->Clear();
213 }
214
215 if (!glueData_.exception_.IsHole()) {
216 visitor(Root::ROOT_VM, ObjectSlot(ToUintPtr(&glueData_.exception_)));
217 }
218 // visit global Constant
219 glueData_.globalConst_.VisitRangeSlot(rangeVisitor);
220 // visit stack roots
221 FrameHandler frameHandler(this);
222 frameHandler.Iterate(visitor, rangeVisitor, derivedVisitor);
223 // visit tagged handle storage roots
224 if (vm_->GetJSOptions().EnableGlobalLeakCheck()) {
225 IterateHandleWithCheck(visitor, rangeVisitor);
226 } else {
227 if (currentHandleStorageIndex_ != -1) {
228 int32_t nid = currentHandleStorageIndex_;
229 for (int32_t i = 0; i <= nid; ++i) {
230 auto node = handleStorageNodes_.at(i);
231 auto start = node->data();
232 auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
233 rangeVisitor(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
234 }
235 }
236
237 globalStorage_->IterateUsageGlobal([visitor](Node *node) {
238 JSTaggedValue value(node->GetObject());
239 if (value.IsHeapObject()) {
240 visitor(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
241 }
242 });
243 }
244 }
245
IterateHandleWithCheck(const RootVisitor & visitor,const RootRangeVisitor & rangeVisitor)246 void JSThread::IterateHandleWithCheck(const RootVisitor &visitor, const RootRangeVisitor &rangeVisitor)
247 {
248 size_t handleCount = 0;
249 if (currentHandleStorageIndex_ != -1) {
250 int32_t nid = currentHandleStorageIndex_;
251 for (int32_t i = 0; i <= nid; ++i) {
252 auto node = handleStorageNodes_.at(i);
253 auto start = node->data();
254 auto end = (i != nid) ? &(node->data()[NODE_BLOCK_SIZE]) : handleScopeStorageNext_;
255 rangeVisitor(ecmascript::Root::ROOT_HANDLE, ObjectSlot(ToUintPtr(start)), ObjectSlot(ToUintPtr(end)));
256 handleCount += (ToUintPtr(end) - ToUintPtr(start)) / sizeof(JSTaggedType);
257 }
258 }
259
260 size_t globalCount = 0;
261 static const int JS_TYPE_LAST = static_cast<int>(JSType::TYPE_LAST);
262 int typeCount[JS_TYPE_LAST] = { 0 };
263 int primitiveCount = 0;
264 bool isStopObjectLeakCheck = EnableGlobalObjectLeakCheck() && !IsStartGlobalLeakCheck() && stackTraceFd_ > 0;
265 bool isStopPrimitiveLeakCheck = EnableGlobalPrimitiveLeakCheck() && !IsStartGlobalLeakCheck() && stackTraceFd_ > 0;
266 std::ostringstream buffer;
267 globalDebugStorage_->IterateUsageGlobal([this, visitor, &globalCount, &typeCount, &primitiveCount,
268 isStopObjectLeakCheck, isStopPrimitiveLeakCheck, &buffer](DebugNode *node) {
269 node->MarkCount();
270 JSTaggedValue value(node->GetObject());
271 if (value.IsHeapObject()) {
272 visitor(ecmascript::Root::ROOT_HANDLE, ecmascript::ObjectSlot(node->GetObjectAddress()));
273 TaggedObject *object = value.GetTaggedObject();
274 MarkWord word(value.GetTaggedObject());
275 if (word.IsForwardingAddress()) {
276 object = word.ToForwardingAddress();
277 }
278 typeCount[static_cast<int>(object->GetClass()->GetObjectType())]++;
279
280 // Print global information about possible memory leaks.
281 // You can print the global new stack within the range of the leaked global number.
282 if (isStopObjectLeakCheck && node->GetGlobalNumber() > 0 && node->GetMarkCount() > 0) {
283 buffer << "Global maybe leak object address:" << std::hex << object <<
284 ", type:" << JSHClass::DumpJSType(JSType(object->GetClass()->GetObjectType())) <<
285 ", node address:" << node << ", number:" << std::dec << node->GetGlobalNumber() <<
286 ", markCount:" << node->GetMarkCount();
287 WriteToStackTraceFd(buffer);
288 }
289 } else {
290 primitiveCount++;
291 if (isStopPrimitiveLeakCheck && node->GetGlobalNumber() > 0 && node->GetMarkCount() > 0) {
292 buffer << "Global maybe leak primitive:" << std::hex << value.GetRawData() <<
293 ", node address:" << node << ", number:" << std::dec << node->GetGlobalNumber() <<
294 ", markCount:" << node->GetMarkCount();
295 WriteToStackTraceFd(buffer);
296 }
297 }
298 globalCount++;
299 });
300
301 if (isStopObjectLeakCheck || isStopPrimitiveLeakCheck) {
302 buffer << "Global leak check success!";
303 WriteToStackTraceFd(buffer);
304 CloseStackTraceFd();
305 }
306 // Determine whether memory leakage by checking handle and global count.
307 LOG_ECMA(INFO) << "Iterate root handle count:" << handleCount << ", global handle count:" << globalCount;
308 OPTIONAL_LOG(GetEcmaVM(), INFO) << "Global type Primitive count:" << primitiveCount;
309 // Print global object type statistic.
310 static const int MIN_COUNT_THRESHOLD = 50;
311 for (int i = 0; i < JS_TYPE_LAST; i++) {
312 if (typeCount[i] > MIN_COUNT_THRESHOLD) {
313 OPTIONAL_LOG(GetEcmaVM(), INFO) << "Global type " << JSHClass::DumpJSType(JSType(i))
314 << " count:" << typeCount[i];
315 }
316 }
317 }
318
IterateWeakEcmaGlobalStorage(const WeakRootVisitor & visitor)319 void JSThread::IterateWeakEcmaGlobalStorage(const WeakRootVisitor &visitor)
320 {
321 auto callBack = [this, visitor](WeakNode *node) {
322 JSTaggedValue value(node->GetObject());
323 if (!value.IsHeapObject()) {
324 return;
325 }
326 auto object = value.GetTaggedObject();
327 auto fwd = visitor(object);
328 if (fwd == nullptr) {
329 // undefind
330 node->SetObject(JSTaggedValue::Undefined().GetRawData());
331 auto secondPassCallback = node->GetSecondPassCallback();
332 if (secondPassCallback) {
333 weakNodeSecondPassCallbacks_.push_back(std::make_pair(secondPassCallback,
334 node->GetReference()));
335 }
336 if (!node->CallFirstPassCallback()) {
337 DisposeGlobalHandle(ToUintPtr(node));
338 }
339 } else if (fwd != object) {
340 // update
341 node->SetObject(JSTaggedValue(fwd).GetRawData());
342 }
343 };
344 if (!vm_->GetJSOptions().EnableGlobalLeakCheck()) {
345 globalStorage_->IterateWeakUsageGlobal(callBack);
346 } else {
347 globalDebugStorage_->IterateWeakUsageGlobal(callBack);
348 }
349 }
350
DoStackOverflowCheck(const JSTaggedType * sp)351 bool JSThread::DoStackOverflowCheck(const JSTaggedType *sp)
352 {
353 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
354 if (UNLIKELY(sp <= glueData_.frameBase_ + RESERVE_STACK_SIZE)) {
355 LOG_ECMA(ERROR) << "Stack overflow! Remaining stack size is: " << (sp - glueData_.frameBase_);
356 if (LIKELY(!HasPendingException())) {
357 ObjectFactory *factory = GetEcmaVM()->GetFactory();
358 JSHandle<JSObject> error = factory->GetJSError(base::ErrorType::RANGE_ERROR, "Stack overflow!", false);
359 SetException(error.GetTaggedValue());
360 }
361 return true;
362 }
363 return false;
364 }
365
ExpandHandleStorage()366 uintptr_t *JSThread::ExpandHandleStorage()
367 {
368 uintptr_t *result = nullptr;
369 int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
370 if (currentHandleStorageIndex_ == lastIndex) {
371 auto n = new std::array<JSTaggedType, NODE_BLOCK_SIZE>();
372 handleStorageNodes_.push_back(n);
373 currentHandleStorageIndex_++;
374 result = reinterpret_cast<uintptr_t *>(&n->data()[0]);
375 handleScopeStorageEnd_ = &n->data()[NODE_BLOCK_SIZE];
376 } else {
377 currentHandleStorageIndex_++;
378 auto lastNode = handleStorageNodes_[currentHandleStorageIndex_];
379 result = reinterpret_cast<uintptr_t *>(&lastNode->data()[0]);
380 handleScopeStorageEnd_ = &lastNode->data()[NODE_BLOCK_SIZE];
381 }
382
383 return result;
384 }
385
ShrinkHandleStorage(int prevIndex)386 void JSThread::ShrinkHandleStorage(int prevIndex)
387 {
388 currentHandleStorageIndex_ = prevIndex;
389 int32_t lastIndex = static_cast<int32_t>(handleStorageNodes_.size() - 1);
390 #if ECMASCRIPT_ENABLE_ZAP_MEM
391 uintptr_t size = ToUintPtr(handleScopeStorageEnd_) - ToUintPtr(handleScopeStorageNext_);
392 if (memset_s(handleScopeStorageNext_, size, 0, size) != EOK) {
393 LOG_FULL(FATAL) << "memset_s failed";
394 UNREACHABLE();
395 }
396 for (int32_t i = currentHandleStorageIndex_ + 1; i < lastIndex; i++) {
397 if (memset_s(handleStorageNodes_[i],
398 NODE_BLOCK_SIZE * sizeof(JSTaggedType), 0,
399 NODE_BLOCK_SIZE * sizeof(JSTaggedType)) !=
400 EOK) {
401 LOG_FULL(FATAL) << "memset_s failed";
402 UNREACHABLE();
403 }
404 }
405 #endif
406
407 if (lastIndex > MIN_HANDLE_STORAGE_SIZE && currentHandleStorageIndex_ < MIN_HANDLE_STORAGE_SIZE) {
408 for (int i = MIN_HANDLE_STORAGE_SIZE; i < lastIndex; i++) {
409 auto node = handleStorageNodes_.back();
410 delete node;
411 handleStorageNodes_.pop_back();
412 }
413 }
414 }
415
NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver)416 void JSThread::NotifyStableArrayElementsGuardians(JSHandle<JSObject> receiver)
417 {
418 if (!receiver->GetJSHClass()->IsPrototype()) {
419 return;
420 }
421 if (!stableArrayElementsGuardians_) {
422 return;
423 }
424 auto env = GetEcmaVM()->GetGlobalEnv();
425 if (receiver.GetTaggedValue() == env->GetObjectFunctionPrototype().GetTaggedValue() ||
426 receiver.GetTaggedValue() == env->GetArrayPrototype().GetTaggedValue()) {
427 SetStableArrayElementsGuardians(JSTaggedValue::False());
428 stableArrayElementsGuardians_ = false;
429 }
430 }
431
ResetGuardians()432 void JSThread::ResetGuardians()
433 {
434 SetStableArrayElementsGuardians(JSTaggedValue::True());
435 stableArrayElementsGuardians_ = true;
436 }
437
CheckSwitchDebuggerBCStub()438 void JSThread::CheckSwitchDebuggerBCStub()
439 {
440 auto isDebug = GetEcmaVM()->GetJsDebuggerManager()->IsDebugMode();
441 if (isDebug &&
442 glueData_.bcDebuggerStubEntries_.Get(0) == glueData_.bcDebuggerStubEntries_.Get(1)) {
443 for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
444 auto stubEntry = glueData_.bcStubEntries_.Get(i);
445 auto debuggerStubEbtry = glueData_.bcDebuggerStubEntries_.Get(i);
446 glueData_.bcDebuggerStubEntries_.Set(i, stubEntry);
447 glueData_.bcStubEntries_.Set(i, debuggerStubEbtry);
448 }
449 } else if (!isDebug &&
450 glueData_.bcStubEntries_.Get(0) == glueData_.bcStubEntries_.Get(1)) {
451 for (size_t i = 0; i < BCStubEntries::BC_HANDLER_COUNT; i++) {
452 auto stubEntry = glueData_.bcDebuggerStubEntries_.Get(i);
453 auto debuggerStubEbtry = glueData_.bcStubEntries_.Get(i);
454 glueData_.bcStubEntries_.Set(i, stubEntry);
455 glueData_.bcDebuggerStubEntries_.Set(i, debuggerStubEbtry);
456 }
457 }
458 }
459
CheckSafepoint() const460 bool JSThread::CheckSafepoint() const
461 {
462 if (vmThreadControl_->VMNeedSuspension()) {
463 vmThreadControl_->SuspendVM();
464 }
465 #ifndef NDEBUG
466 if (vm_->GetJSOptions().EnableForceGC()) {
467 GetEcmaVM()->CollectGarbage(TriggerGCType::FULL_GC);
468 return true;
469 }
470 #endif
471 if (IsMarkFinished()) {
472 auto heap = GetEcmaVM()->GetHeap();
473 heap->GetConcurrentMarker()->HandleMarkingFinished();
474 return true;
475 }
476 return false;
477 }
478
CheckJSTaggedType(JSTaggedType value) const479 void JSThread::CheckJSTaggedType(JSTaggedType value) const
480 {
481 if (JSTaggedValue(value).IsHeapObject() &&
482 !GetEcmaVM()->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
483 LOG_FULL(FATAL) << "value:" << value << " is invalid!";
484 }
485 }
486
CpuProfilerCheckJSTaggedType(JSTaggedType value) const487 bool JSThread::CpuProfilerCheckJSTaggedType(JSTaggedType value) const
488 {
489 if (JSTaggedValue(value).IsHeapObject() &&
490 !GetEcmaVM()->GetHeap()->IsAlive(reinterpret_cast<TaggedObject *>(value))) {
491 return false;
492 }
493 return true;
494 }
495
CollectBCOffsetInfo()496 void JSThread::CollectBCOffsetInfo()
497 {
498 FrameBcCollector collector(this);
499 collector.CollectBCOffsetInfo();
500 }
501
502 // static
GetAsmStackLimit()503 size_t JSThread::GetAsmStackLimit()
504 {
505 #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) && !defined(PANDA_TARGET_IOS)
506 // js stack limit
507 size_t result = GetCurrentStackPosition() - EcmaParamConfiguration::GetDefalutStackSize();
508 pthread_attr_t attr;
509 int ret = pthread_getattr_np(pthread_self(), &attr);
510 if (ret != 0) {
511 LOG_ECMA(ERROR) << "Get current thread attr failed";
512 return result;
513 }
514
515 void *stackAddr = nullptr;
516 size_t size = 0;
517 ret = pthread_attr_getstack(&attr, &stackAddr, &size);
518 if (ret != 0) {
519 LOG_ECMA(ERROR) << "Get current thread stack size failed";
520 if (pthread_attr_destroy(&attr) != 0) {
521 LOG_ECMA(ERROR) << "Destroy current thread attr failed";
522 }
523 return result;
524 }
525
526 uintptr_t threadStackLimit = reinterpret_cast<uintptr_t>(stackAddr);
527 if (result < threadStackLimit) {
528 result = threadStackLimit;
529 }
530
531 uintptr_t threadStackStart = threadStackLimit + size;
532 LOG_INTERPRETER(INFO) << "Current thread stack start: " << reinterpret_cast<void *>(threadStackStart);
533 LOG_INTERPRETER(INFO) << "Used stack before js stack start: "
534 << reinterpret_cast<void *>(threadStackStart - GetCurrentStackPosition());
535 LOG_INTERPRETER(INFO) << "Current thread asm stack limit: " << reinterpret_cast<void *>(result);
536 ret = pthread_attr_destroy(&attr);
537 if (ret != 0) {
538 LOG_ECMA(ERROR) << "Destroy current thread attr failed";
539 }
540
541 // To avoid too much times of stack overflow checking, we only check stack overflow before push vregs or
542 // parameters of variable length. So we need a reserved size of stack to make sure stack won't be overflowed
543 // when push other data.
544 result += EcmaParamConfiguration::GetDefaultReservedStackSize();
545 if (threadStackStart <= result) {
546 LOG_FULL(FATAL) << "Too small stackSize to run jsvm";
547 }
548 return result;
549 #else
550 return 0;
551 #endif
552 }
553
IsLegalAsmSp(uintptr_t sp) const554 bool JSThread::IsLegalAsmSp(uintptr_t sp) const
555 {
556 uint64_t bottom = GetStackLimit() - EcmaParamConfiguration::GetDefaultReservedStackSize();
557 uint64_t top = GetStackStart();
558 return (bottom <= sp && sp <= top);
559 }
560
IsLegalThreadSp(uintptr_t sp) const561 bool JSThread::IsLegalThreadSp(uintptr_t sp) const
562 {
563 uintptr_t bottom = reinterpret_cast<uintptr_t>(glueData_.frameBase_);
564 size_t maxStackSize = vm_->GetEcmaParamConfiguration().GetMaxStackSize();
565 uintptr_t top = bottom + maxStackSize;
566 return (bottom <= sp && sp <= top);
567 }
568
IsLegalSp(uintptr_t sp) const569 bool JSThread::IsLegalSp(uintptr_t sp) const
570 {
571 return IsLegalAsmSp(sp) || IsLegalThreadSp(sp);
572 }
573 } // namespace panda::ecmascript
574