2 * Copyright (C) 2003-2009, 2015 Apple Inc. All rights reserved.
3 * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
4 * Copyright (C) 2009 Acision BV. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "MachineStackMarker.h"
25 #include "ConservativeRoots.h"
28 #include "JSCInlines.h"
32 #include <wtf/StdLibExtras.h>
36 #include <mach/mach_init.h>
37 #include <mach/mach_port.h>
38 #include <mach/task.h>
39 #include <mach/thread_act.h>
40 #include <mach/vm_map.h>
58 #if HAVE(PTHREAD_NP_H)
59 #include <pthread_np.h>
62 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
73 typedef mach_port_t PlatformThread
;
75 typedef DWORD PlatformThread
;
77 typedef pthread_t PlatformThread
;
78 static const int SigThreadSuspendResume
= SIGUSR2
;
80 #if defined(SA_RESTART)
81 static void pthreadSignalHandlerSuspendResume(int)
84 sigemptyset(&signalSet
);
85 sigaddset(&signalSet
, SigThreadSuspendResume
);
86 sigsuspend(&signalSet
);
91 class ActiveMachineThreadsManager
;
92 static ActiveMachineThreadsManager
& activeMachineThreadsManager();
94 class ActiveMachineThreadsManager
{
95 WTF_MAKE_NONCOPYABLE(ActiveMachineThreadsManager
);
100 Locker(ActiveMachineThreadsManager
& manager
)
101 : m_locker(manager
.m_lock
)
106 MutexLocker m_locker
;
109 void add(MachineThreads
* machineThreads
)
111 MutexLocker
managerLock(m_lock
);
112 m_set
.add(machineThreads
);
115 void remove(MachineThreads
* machineThreads
)
117 MutexLocker
managerLock(m_lock
);
118 auto recordedMachineThreads
= m_set
.take(machineThreads
);
119 RELEASE_ASSERT(recordedMachineThreads
= machineThreads
);
122 bool contains(MachineThreads
* machineThreads
)
124 return m_set
.contains(machineThreads
);
128 typedef HashSet
<MachineThreads
*> MachineThreadsSet
;
130 ActiveMachineThreadsManager() { }
133 MachineThreadsSet m_set
;
135 friend ActiveMachineThreadsManager
& activeMachineThreadsManager();
138 static ActiveMachineThreadsManager
& activeMachineThreadsManager()
140 static std::once_flag initializeManagerOnceFlag
;
141 static ActiveMachineThreadsManager
* manager
= nullptr;
143 std::call_once(initializeManagerOnceFlag
, [] {
144 manager
= new ActiveMachineThreadsManager();
149 static inline PlatformThread
getCurrentPlatformThread()
152 return pthread_mach_thread_np(pthread_self());
154 return GetCurrentThreadId();
156 return pthread_self();
160 class MachineThreads::Thread
{
161 WTF_MAKE_FAST_ALLOCATED
;
163 Thread(const PlatformThread
& platThread
, void* base
)
164 : platformThread(platThread
)
167 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART)
168 // if we have SA_RESTART, enable SIGUSR2 debugging mechanism
169 struct sigaction action
;
170 action
.sa_handler
= pthreadSignalHandlerSuspendResume
;
171 sigemptyset(&action
.sa_mask
);
172 action
.sa_flags
= SA_RESTART
;
173 sigaction(SigThreadSuspendResume
, &action
, 0);
177 sigaddset(&mask
, SigThreadSuspendResume
);
178 pthread_sigmask(SIG_UNBLOCK
, &mask
, 0);
180 ASSERT(platformThread
== GetCurrentThreadId());
182 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
183 &platformThreadHandle
, 0, FALSE
, DUPLICATE_SAME_ACCESS
);
184 RELEASE_ASSERT(isSuccessful
);
192 CloseHandle(platformThreadHandle
);
196 static Thread
* createForCurrentThread()
198 return new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin());
202 inline void* stackPointer() const;
206 typedef i386_thread_state_t PlatformRegisters
;
208 typedef x86_thread_state64_t PlatformRegisters
;
210 typedef ppc_thread_state_t PlatformRegisters
;
212 typedef ppc_thread_state64_t PlatformRegisters
;
214 typedef arm_thread_state_t PlatformRegisters
;
216 typedef arm_thread_state64_t PlatformRegisters
;
218 #error Unknown Architecture
222 typedef CONTEXT PlatformRegisters
;
224 typedef pthread_attr_t PlatformRegisters
;
226 #error Need a thread register struct for this platform
229 PlatformRegisters regs
;
232 inline bool operator==(const PlatformThread
& other
) const;
233 inline bool operator!=(const PlatformThread
& other
) const { return !(*this == other
); }
235 inline bool suspend();
236 inline void resume();
237 size_t getRegisters(Registers
&);
238 void freeRegisters(Registers
&);
239 std::pair
<void*, size_t> captureStack(void* stackTop
);
242 PlatformThread platformThread
;
245 HANDLE platformThreadHandle
;
249 MachineThreads::MachineThreads(Heap
* heap
)
250 : m_registeredThreads(0)
251 , m_threadSpecific(0)
257 threadSpecificKeyCreate(&m_threadSpecific
, removeThread
);
258 activeMachineThreadsManager().add(this);
261 MachineThreads::~MachineThreads()
263 activeMachineThreadsManager().remove(this);
264 threadSpecificKeyDelete(m_threadSpecific
);
266 MutexLocker
registeredThreadsLock(m_registeredThreadsMutex
);
267 for (Thread
* t
= m_registeredThreads
; t
;) {
268 Thread
* next
= t
->next
;
274 inline bool MachineThreads::Thread::operator==(const PlatformThread
& other
) const
276 #if OS(DARWIN) || OS(WINDOWS)
277 return platformThread
== other
;
279 return !!pthread_equal(platformThread
, other
);
281 #error Need a way to compare threads on this platform
285 void MachineThreads::addCurrentThread()
287 ASSERT(!m_heap
->vm()->hasExclusiveThread() || m_heap
->vm()->exclusiveThread() == std::this_thread::get_id());
289 if (threadSpecificGet(m_threadSpecific
)) {
290 ASSERT(threadSpecificGet(m_threadSpecific
) == this);
294 threadSpecificSet(m_threadSpecific
, this);
295 Thread
* thread
= Thread::createForCurrentThread();
297 MutexLocker
lock(m_registeredThreadsMutex
);
299 thread
->next
= m_registeredThreads
;
300 m_registeredThreads
= thread
;
303 void MachineThreads::removeThread(void* p
)
305 auto& manager
= activeMachineThreadsManager();
306 ActiveMachineThreadsManager::Locker
lock(manager
);
307 auto machineThreads
= static_cast<MachineThreads
*>(p
);
308 if (manager
.contains(machineThreads
)) {
309 // There's a chance that the MachineThreads registry that this thread
310 // was registered with was already destructed, and another one happened
311 // to be instantiated at the same address. Hence, this thread may or
312 // may not be found in this MachineThreads registry. We only need to
313 // do a removal if this thread is found in it.
314 machineThreads
->removeThreadIfFound(getCurrentPlatformThread());
318 template<typename PlatformThread
>
319 void MachineThreads::removeThreadIfFound(PlatformThread platformThread
)
321 MutexLocker
lock(m_registeredThreadsMutex
);
322 Thread
* t
= m_registeredThreads
;
323 if (*t
== platformThread
) {
324 m_registeredThreads
= m_registeredThreads
->next
;
327 Thread
* last
= m_registeredThreads
;
328 for (t
= m_registeredThreads
->next
; t
; t
= t
->next
) {
329 if (*t
== platformThread
) {
330 last
->next
= t
->next
;
339 void MachineThreads::gatherFromCurrentThread(ConservativeRoots
& conservativeRoots
, JITStubRoutineSet
& jitStubRoutines
, CodeBlockSet
& codeBlocks
, void* stackOrigin
, void* stackTop
, RegisterState
& calleeSavedRegisters
)
341 void* registersBegin
= &calleeSavedRegisters
;
342 void* registersEnd
= reinterpret_cast<void*>(roundUpToMultipleOf
<sizeof(void*)>(reinterpret_cast<uintptr_t>(&calleeSavedRegisters
+ 1)));
343 conservativeRoots
.add(registersBegin
, registersEnd
, jitStubRoutines
, codeBlocks
);
345 conservativeRoots
.add(stackTop
, stackOrigin
, jitStubRoutines
, codeBlocks
);
348 inline bool MachineThreads::Thread::suspend()
351 kern_return_t result
= thread_suspend(platformThread
);
352 return result
== KERN_SUCCESS
;
354 bool threadIsSuspended
= (SuspendThread(platformThreadHandle
) != (DWORD
)-1);
355 ASSERT(threadIsSuspended
);
356 return threadIsSuspended
;
358 pthread_kill(platformThread
, SigThreadSuspendResume
);
361 #error Need a way to suspend threads on this platform
365 inline void MachineThreads::Thread::resume()
368 thread_resume(platformThread
);
370 ResumeThread(platformThreadHandle
);
372 pthread_kill(platformThread
, SigThreadSuspendResume
);
374 #error Need a way to resume threads on this platform
378 size_t MachineThreads::Thread::getRegisters(MachineThreads::Thread::Registers
& registers
)
380 Thread::Registers::PlatformRegisters
& regs
= registers
.regs
;
383 unsigned user_count
= sizeof(regs
)/sizeof(int);
384 thread_state_flavor_t flavor
= i386_THREAD_STATE
;
386 unsigned user_count
= x86_THREAD_STATE64_COUNT
;
387 thread_state_flavor_t flavor
= x86_THREAD_STATE64
;
389 unsigned user_count
= PPC_THREAD_STATE_COUNT
;
390 thread_state_flavor_t flavor
= PPC_THREAD_STATE
;
392 unsigned user_count
= PPC_THREAD_STATE64_COUNT
;
393 thread_state_flavor_t flavor
= PPC_THREAD_STATE64
;
395 unsigned user_count
= ARM_THREAD_STATE_COUNT
;
396 thread_state_flavor_t flavor
= ARM_THREAD_STATE
;
398 unsigned user_count
= ARM_THREAD_STATE64_COUNT
;
399 thread_state_flavor_t flavor
= ARM_THREAD_STATE64
;
401 #error Unknown Architecture
404 kern_return_t result
= thread_get_state(platformThread
, flavor
, (thread_state_t
)®s
, &user_count
);
405 if (result
!= KERN_SUCCESS
) {
406 WTFReportFatalError(__FILE__
, __LINE__
, WTF_PRETTY_FUNCTION
,
407 "JavaScript garbage collection failed because thread_get_state returned an error (%d). This is probably the result of running inside Rosetta, which is not supported.", result
);
410 return user_count
* sizeof(uintptr_t);
414 regs
.ContextFlags
= CONTEXT_INTEGER
| CONTEXT_CONTROL
;
415 GetThreadContext(platformThreadHandle
, ®s
);
416 return sizeof(CONTEXT
);
418 pthread_attr_init(®s
);
419 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
421 // e.g. on FreeBSD 5.4, neundorf@kde.org
422 pthread_attr_get_np(platformThread
, ®s
);
425 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
426 pthread_getattr_np(platformThread
, ®s
);
430 #error Need a way to get thread registers on this platform
434 inline void* MachineThreads::Thread::Registers::stackPointer() const
441 return reinterpret_cast<void*>(regs
.__esp
);
443 return reinterpret_cast<void*>(regs
.__rsp
);
444 #elif CPU(PPC) || CPU(PPC64)
445 return reinterpret_cast<void*>(regs
.__r1
);
447 return reinterpret_cast<void*>(regs
.__sp
);
449 return reinterpret_cast<void*>(regs
.__sp
);
451 #error Unknown Architecture
454 #else // !__DARWIN_UNIX03
457 return reinterpret_cast<void*>(regs
.esp
);
459 return reinterpret_cast<void*>(regs
.rsp
);
460 #elif CPU(PPC) || CPU(PPC64)
461 return reinterpret_cast<void*>(regs
.r1
);
463 #error Unknown Architecture
466 #endif // __DARWIN_UNIX03
472 return reinterpret_cast<void*>((uintptr_t) regs
.Sp
);
474 return reinterpret_cast<void*>((uintptr_t) regs
.IntSp
);
476 return reinterpret_cast<void*>((uintptr_t) regs
.Esp
);
478 return reinterpret_cast<void*>((uintptr_t) regs
.Rsp
);
480 #error Unknown Architecture
485 size_t stackSize
= 0;
488 int rc
= pthread_stackseg_np(pthread_self(), &ss
);
489 stackBase
= (void*)((size_t) ss
.ss_sp
- ss
.ss_size
);
490 stackSize
= ss
.ss_size
;
492 int rc
= pthread_attr_getstack(®s
, &stackBase
, &stackSize
);
494 (void)rc
; // FIXME: Deal with error code somehow? Seems fatal.
496 return static_cast<char*>(stackBase
) + stackSize
;
498 #error Need a way to get the stack pointer for another thread on this platform
502 void MachineThreads::Thread::freeRegisters(MachineThreads::Thread::Registers
& registers
)
504 Thread::Registers::PlatformRegisters
& regs
= registers
.regs
;
505 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
506 pthread_attr_destroy(®s
);
512 std::pair
<void*, size_t> MachineThreads::Thread::captureStack(void* stackTop
)
514 void* begin
= stackBase
;
515 void* end
= reinterpret_cast<void*>(
516 WTF::roundUpToMultipleOf
<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackTop
)));
518 std::swap(begin
, end
);
519 return std::make_pair(begin
, static_cast<char*>(end
) - static_cast<char*>(begin
));
522 static void copyMemory(void* dst
, const void* src
, size_t size
)
524 size_t dstAsSize
= reinterpret_cast<size_t>(dst
);
525 size_t srcAsSize
= reinterpret_cast<size_t>(src
);
526 RELEASE_ASSERT(dstAsSize
== WTF::roundUpToMultipleOf
<sizeof(intptr_t)>(dstAsSize
));
527 RELEASE_ASSERT(srcAsSize
== WTF::roundUpToMultipleOf
<sizeof(intptr_t)>(srcAsSize
));
528 RELEASE_ASSERT(size
== WTF::roundUpToMultipleOf
<sizeof(intptr_t)>(size
));
530 intptr_t* dstPtr
= reinterpret_cast<intptr_t*>(dst
);
531 const intptr_t* srcPtr
= reinterpret_cast<const intptr_t*>(src
);
532 size
/= sizeof(intptr_t);
534 *dstPtr
++ = *srcPtr
++;
539 // This function must not call malloc(), free(), or any other function that might
540 // acquire a lock. Since 'thread' is suspended, trying to acquire a lock
541 // will deadlock if 'thread' holds that lock.
542 // This function, specifically the memory copying, was causing problems with Address Sanitizer in
543 // apps. Since we cannot blacklist the system memcpy we must use our own naive implementation,
544 // copyMemory, for ASan to work on either instrumented or non-instrumented builds. This is not a
545 // significant performance loss as tryCopyOtherThreadStack is only called as part of an O(heapsize)
546 // operation. As the heap is generally much larger than the stack the performance hit is minimal.
547 // See: https://bugs.webkit.org/show_bug.cgi?id=146297
548 void MachineThreads::tryCopyOtherThreadStack(Thread
* thread
, void* buffer
, size_t capacity
, size_t* size
)
550 Thread::Registers registers
;
551 size_t registersSize
= thread
->getRegisters(registers
);
552 std::pair
<void*, size_t> stack
= thread
->captureStack(registers
.stackPointer());
554 bool canCopy
= *size
+ registersSize
+ stack
.second
<= capacity
;
557 copyMemory(static_cast<char*>(buffer
) + *size
, ®isters
, registersSize
);
558 *size
+= registersSize
;
561 copyMemory(static_cast<char*>(buffer
) + *size
, stack
.first
, stack
.second
);
562 *size
+= stack
.second
;
564 thread
->freeRegisters(registers
);
567 bool MachineThreads::tryCopyOtherThreadStacks(MutexLocker
&, void* buffer
, size_t capacity
, size_t* size
)
569 // Prevent two VMs from suspending each other's threads at the same time,
570 // which can cause deadlock: <rdar://problem/20300842>.
571 static StaticSpinLock mutex
;
572 std::lock_guard
<StaticSpinLock
> lock(mutex
);
576 PlatformThread currentPlatformThread
= getCurrentPlatformThread();
577 int numberOfThreads
= 0; // Using 0 to denote that we haven't counted the number of threads yet.
579 Thread
* threadsToBeDeleted
= nullptr;
581 Thread
* previousThread
= nullptr;
582 for (Thread
* thread
= m_registeredThreads
; thread
; index
++) {
583 if (*thread
!= currentPlatformThread
) {
584 bool success
= thread
->suspend();
587 if (!numberOfThreads
) {
588 for (Thread
* countedThread
= m_registeredThreads
; countedThread
; countedThread
= countedThread
->next
)
592 // Re-do the suspension to get the actual failure result for logging.
593 kern_return_t error
= thread_suspend(thread
->platformThread
);
594 ASSERT(error
!= KERN_SUCCESS
);
596 WTFReportError(__FILE__
, __LINE__
, WTF_PRETTY_FUNCTION
,
597 "JavaScript garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] platformThread %p.",
598 error
, index
, numberOfThreads
, thread
, reinterpret_cast<void*>(thread
->platformThread
));
600 // Put the invalid thread on the threadsToBeDeleted list.
601 // We can't just delete it here because we have suspended other
602 // threads, and they may still be holding the C heap lock which
603 // we need for deleting the invalid thread. Hence, we need to
604 // defer the deletion till after we have resumed all threads.
605 Thread
* nextThread
= thread
->next
;
606 thread
->next
= threadsToBeDeleted
;
607 threadsToBeDeleted
= thread
;
610 previousThread
->next
= nextThread
;
612 m_registeredThreads
= nextThread
;
617 UNUSED_PARAM(numberOfThreads
);
618 UNUSED_PARAM(previousThread
);
619 ASSERT_UNUSED(success
, success
);
622 previousThread
= thread
;
623 thread
= thread
->next
;
626 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
627 if (*thread
!= currentPlatformThread
)
628 tryCopyOtherThreadStack(thread
, buffer
, capacity
, size
);
631 for (Thread
* thread
= m_registeredThreads
; thread
; thread
= thread
->next
) {
632 if (*thread
!= currentPlatformThread
)
636 for (Thread
* thread
= threadsToBeDeleted
; thread
; ) {
637 Thread
* nextThread
= thread
->next
;
642 return *size
<= capacity
;
645 static void growBuffer(size_t size
, void** buffer
, size_t* capacity
)
650 *capacity
= WTF::roundUpToMultipleOf(WTF::pageSize(), size
* 2);
651 *buffer
= fastMalloc(*capacity
);
654 void MachineThreads::gatherConservativeRoots(ConservativeRoots
& conservativeRoots
, JITStubRoutineSet
& jitStubRoutines
, CodeBlockSet
& codeBlocks
, void* stackOrigin
, void* stackTop
, RegisterState
& calleeSavedRegisters
)
656 gatherFromCurrentThread(conservativeRoots
, jitStubRoutines
, codeBlocks
, stackOrigin
, stackTop
, calleeSavedRegisters
);
660 void* buffer
= nullptr;
661 MutexLocker
lock(m_registeredThreadsMutex
);
662 while (!tryCopyOtherThreadStacks(lock
, buffer
, capacity
, &size
))
663 growBuffer(size
, &buffer
, &capacity
);
668 conservativeRoots
.add(buffer
, static_cast<char*>(buffer
) + size
, jitStubRoutines
, codeBlocks
);