]>
Commit | Line | Data |
---|---|---|
14957cd0 | 1 | /* |
ed1e77d3 | 2 | * Copyright (C) 2003-2009, 2015 Apple Inc. All rights reserved. |
14957cd0 A |
3 | * Copyright (C) 2007 Eric Seidel <eric@webkit.org> |
4 | * Copyright (C) 2009 Acision BV. All rights reserved. | |
5 | * | |
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. | |
10 | * | |
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. | |
15 | * | |
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 | |
19 | * | |
20 | */ | |
21 | ||
22 | #include "config.h" | |
23 | #include "MachineStackMarker.h" | |
24 | ||
25 | #include "ConservativeRoots.h" | |
26 | #include "Heap.h" | |
27 | #include "JSArray.h" | |
81345200 | 28 | #include "JSCInlines.h" |
93a37866 | 29 | #include "VM.h" |
14957cd0 A |
30 | #include <setjmp.h> |
31 | #include <stdlib.h> | |
32 | #include <wtf/StdLibExtras.h> | |
33 | ||
14957cd0 A |
34 | #if OS(DARWIN) |
35 | ||
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> | |
41 | ||
42 | #elif OS(WINDOWS) | |
43 | ||
44 | #include <windows.h> | |
45 | #include <malloc.h> | |
46 | ||
14957cd0 A |
47 | #elif OS(UNIX) |
48 | ||
14957cd0 | 49 | #include <sys/mman.h> |
14957cd0 A |
50 | #include <unistd.h> |
51 | ||
52 | #if OS(SOLARIS) | |
53 | #include <thread.h> | |
54 | #else | |
55 | #include <pthread.h> | |
56 | #endif | |
57 | ||
58 | #if HAVE(PTHREAD_NP_H) | |
59 | #include <pthread_np.h> | |
60 | #endif | |
61 | ||
14957cd0 A |
62 | #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) |
63 | #include <signal.h> | |
14957cd0 A |
64 | #endif |
65 | ||
66 | #endif | |
67 | ||
68 | using namespace WTF; | |
69 | ||
70 | namespace JSC { | |
71 | ||
14957cd0 A |
72 | #if OS(DARWIN) |
73 | typedef mach_port_t PlatformThread; | |
74 | #elif OS(WINDOWS) | |
ed1e77d3 | 75 | typedef DWORD PlatformThread; |
14957cd0 A |
76 | #elif USE(PTHREADS) |
77 | typedef pthread_t PlatformThread; | |
78 | static const int SigThreadSuspendResume = SIGUSR2; | |
79 | ||
6fe7ccc8 | 80 | #if defined(SA_RESTART) |
93a37866 | 81 | static void pthreadSignalHandlerSuspendResume(int) |
14957cd0 A |
82 | { |
83 | sigset_t signalSet; | |
84 | sigemptyset(&signalSet); | |
85 | sigaddset(&signalSet, SigThreadSuspendResume); | |
86 | sigsuspend(&signalSet); | |
87 | } | |
88 | #endif | |
6fe7ccc8 | 89 | #endif |
14957cd0 | 90 | |
ed1e77d3 A |
91 | class ActiveMachineThreadsManager; |
92 | static ActiveMachineThreadsManager& activeMachineThreadsManager(); | |
93 | ||
94 | class ActiveMachineThreadsManager { | |
95 | WTF_MAKE_NONCOPYABLE(ActiveMachineThreadsManager); | |
96 | public: | |
97 | ||
98 | class Locker { | |
99 | public: | |
100 | Locker(ActiveMachineThreadsManager& manager) | |
101 | : m_locker(manager.m_lock) | |
102 | { | |
103 | } | |
104 | ||
105 | private: | |
106 | MutexLocker m_locker; | |
107 | }; | |
108 | ||
109 | void add(MachineThreads* machineThreads) | |
110 | { | |
111 | MutexLocker managerLock(m_lock); | |
112 | m_set.add(machineThreads); | |
113 | } | |
114 | ||
115 | void remove(MachineThreads* machineThreads) | |
116 | { | |
117 | MutexLocker managerLock(m_lock); | |
118 | auto recordedMachineThreads = m_set.take(machineThreads); | |
119 | RELEASE_ASSERT(recordedMachineThreads = machineThreads); | |
120 | } | |
121 | ||
122 | bool contains(MachineThreads* machineThreads) | |
123 | { | |
124 | return m_set.contains(machineThreads); | |
125 | } | |
126 | ||
127 | private: | |
128 | typedef HashSet<MachineThreads*> MachineThreadsSet; | |
129 | ||
130 | ActiveMachineThreadsManager() { } | |
131 | ||
132 | Mutex m_lock; | |
133 | MachineThreadsSet m_set; | |
134 | ||
135 | friend ActiveMachineThreadsManager& activeMachineThreadsManager(); | |
136 | }; | |
137 | ||
138 | static ActiveMachineThreadsManager& activeMachineThreadsManager() | |
139 | { | |
140 | static std::once_flag initializeManagerOnceFlag; | |
141 | static ActiveMachineThreadsManager* manager = nullptr; | |
142 | ||
143 | std::call_once(initializeManagerOnceFlag, [] { | |
144 | manager = new ActiveMachineThreadsManager(); | |
145 | }); | |
146 | return *manager; | |
147 | } | |
148 | ||
149 | static inline PlatformThread getCurrentPlatformThread() | |
150 | { | |
151 | #if OS(DARWIN) | |
152 | return pthread_mach_thread_np(pthread_self()); | |
153 | #elif OS(WINDOWS) | |
154 | return GetCurrentThreadId(); | |
155 | #elif USE(PTHREADS) | |
156 | return pthread_self(); | |
157 | #endif | |
158 | } | |
159 | ||
14957cd0 | 160 | class MachineThreads::Thread { |
93a37866 | 161 | WTF_MAKE_FAST_ALLOCATED; |
ed1e77d3 | 162 | |
6fe7ccc8 A |
163 | Thread(const PlatformThread& platThread, void* base) |
164 | : platformThread(platThread) | |
14957cd0 A |
165 | , stackBase(base) |
166 | { | |
6fe7ccc8 A |
167 | #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART) |
168 | // if we have SA_RESTART, enable SIGUSR2 debugging mechanism | |
14957cd0 A |
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); | |
174 | ||
175 | sigset_t mask; | |
176 | sigemptyset(&mask); | |
177 | sigaddset(&mask, SigThreadSuspendResume); | |
178 | pthread_sigmask(SIG_UNBLOCK, &mask, 0); | |
ed1e77d3 A |
179 | #elif OS(WINDOWS) |
180 | ASSERT(platformThread == GetCurrentThreadId()); | |
181 | bool isSuccessful = | |
182 | DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), | |
183 | &platformThreadHandle, 0, FALSE, DUPLICATE_SAME_ACCESS); | |
184 | RELEASE_ASSERT(isSuccessful); | |
185 | #endif | |
186 | } | |
187 | ||
188 | public: | |
189 | ~Thread() | |
190 | { | |
191 | #if OS(WINDOWS) | |
192 | CloseHandle(platformThreadHandle); | |
14957cd0 A |
193 | #endif |
194 | } | |
195 | ||
ed1e77d3 A |
196 | static Thread* createForCurrentThread() |
197 | { | |
198 | return new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin()); | |
199 | } | |
200 | ||
201 | struct Registers { | |
202 | inline void* stackPointer() const; | |
203 | ||
204 | #if OS(DARWIN) | |
205 | #if CPU(X86) | |
206 | typedef i386_thread_state_t PlatformRegisters; | |
207 | #elif CPU(X86_64) | |
208 | typedef x86_thread_state64_t PlatformRegisters; | |
209 | #elif CPU(PPC) | |
210 | typedef ppc_thread_state_t PlatformRegisters; | |
211 | #elif CPU(PPC64) | |
212 | typedef ppc_thread_state64_t PlatformRegisters; | |
213 | #elif CPU(ARM) | |
214 | typedef arm_thread_state_t PlatformRegisters; | |
215 | #elif CPU(ARM64) | |
216 | typedef arm_thread_state64_t PlatformRegisters; | |
217 | #else | |
218 | #error Unknown Architecture | |
219 | #endif | |
220 | ||
221 | #elif OS(WINDOWS) | |
222 | typedef CONTEXT PlatformRegisters; | |
223 | #elif USE(PTHREADS) | |
224 | typedef pthread_attr_t PlatformRegisters; | |
225 | #else | |
226 | #error Need a thread register struct for this platform | |
227 | #endif | |
228 | ||
229 | PlatformRegisters regs; | |
230 | }; | |
231 | ||
232 | inline bool operator==(const PlatformThread& other) const; | |
233 | inline bool operator!=(const PlatformThread& other) const { return !(*this == other); } | |
234 | ||
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); | |
240 | ||
14957cd0 | 241 | Thread* next; |
14957cd0 A |
242 | PlatformThread platformThread; |
243 | void* stackBase; | |
ed1e77d3 A |
244 | #if OS(WINDOWS) |
245 | HANDLE platformThreadHandle; | |
246 | #endif | |
14957cd0 A |
247 | }; |
248 | ||
14957cd0 | 249 | MachineThreads::MachineThreads(Heap* heap) |
93a37866 | 250 | : m_registeredThreads(0) |
14957cd0 | 251 | , m_threadSpecific(0) |
93a37866 A |
252 | #if !ASSERT_DISABLED |
253 | , m_heap(heap) | |
254 | #endif | |
14957cd0 | 255 | { |
93a37866 | 256 | UNUSED_PARAM(heap); |
ed1e77d3 A |
257 | threadSpecificKeyCreate(&m_threadSpecific, removeThread); |
258 | activeMachineThreadsManager().add(this); | |
14957cd0 A |
259 | } |
260 | ||
261 | MachineThreads::~MachineThreads() | |
262 | { | |
ed1e77d3 A |
263 | activeMachineThreadsManager().remove(this); |
264 | threadSpecificKeyDelete(m_threadSpecific); | |
14957cd0 A |
265 | |
266 | MutexLocker registeredThreadsLock(m_registeredThreadsMutex); | |
267 | for (Thread* t = m_registeredThreads; t;) { | |
268 | Thread* next = t->next; | |
269 | delete t; | |
270 | t = next; | |
271 | } | |
14957cd0 A |
272 | } |
273 | ||
ed1e77d3 | 274 | inline bool MachineThreads::Thread::operator==(const PlatformThread& other) const |
6fe7ccc8 A |
275 | { |
276 | #if OS(DARWIN) || OS(WINDOWS) | |
ed1e77d3 | 277 | return platformThread == other; |
6fe7ccc8 | 278 | #elif USE(PTHREADS) |
ed1e77d3 | 279 | return !!pthread_equal(platformThread, other); |
6fe7ccc8 A |
280 | #else |
281 | #error Need a way to compare threads on this platform | |
282 | #endif | |
283 | } | |
284 | ||
14957cd0 A |
285 | void MachineThreads::addCurrentThread() |
286 | { | |
81345200 | 287 | ASSERT(!m_heap->vm()->hasExclusiveThread() || m_heap->vm()->exclusiveThread() == std::this_thread::get_id()); |
14957cd0 | 288 | |
ed1e77d3 A |
289 | if (threadSpecificGet(m_threadSpecific)) { |
290 | ASSERT(threadSpecificGet(m_threadSpecific) == this); | |
14957cd0 | 291 | return; |
ed1e77d3 | 292 | } |
14957cd0 | 293 | |
93a37866 | 294 | threadSpecificSet(m_threadSpecific, this); |
ed1e77d3 | 295 | Thread* thread = Thread::createForCurrentThread(); |
14957cd0 A |
296 | |
297 | MutexLocker lock(m_registeredThreadsMutex); | |
298 | ||
299 | thread->next = m_registeredThreads; | |
300 | m_registeredThreads = thread; | |
301 | } | |
302 | ||
303 | void MachineThreads::removeThread(void* p) | |
304 | { | |
ed1e77d3 A |
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()); | |
315 | } | |
14957cd0 A |
316 | } |
317 | ||
ed1e77d3 A |
318 | template<typename PlatformThread> |
319 | void MachineThreads::removeThreadIfFound(PlatformThread platformThread) | |
14957cd0 | 320 | { |
14957cd0 | 321 | MutexLocker lock(m_registeredThreadsMutex); |
ed1e77d3 A |
322 | Thread* t = m_registeredThreads; |
323 | if (*t == platformThread) { | |
14957cd0 A |
324 | m_registeredThreads = m_registeredThreads->next; |
325 | delete t; | |
326 | } else { | |
327 | Thread* last = m_registeredThreads; | |
14957cd0 | 328 | for (t = m_registeredThreads->next; t; t = t->next) { |
ed1e77d3 | 329 | if (*t == platformThread) { |
14957cd0 A |
330 | last->next = t->next; |
331 | break; | |
332 | } | |
333 | last = t; | |
334 | } | |
14957cd0 A |
335 | delete t; |
336 | } | |
337 | } | |
ed1e77d3 A |
338 | |
339 | void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackOrigin, void* stackTop, RegisterState& calleeSavedRegisters) | |
14957cd0 | 340 | { |
ed1e77d3 A |
341 | void* registersBegin = &calleeSavedRegisters; |
342 | void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(&calleeSavedRegisters + 1))); | |
81345200 | 343 | conservativeRoots.add(registersBegin, registersEnd, jitStubRoutines, codeBlocks); |
14957cd0 | 344 | |
ed1e77d3 | 345 | conservativeRoots.add(stackTop, stackOrigin, jitStubRoutines, codeBlocks); |
14957cd0 A |
346 | } |
347 | ||
ed1e77d3 | 348 | inline bool MachineThreads::Thread::suspend() |
14957cd0 A |
349 | { |
350 | #if OS(DARWIN) | |
40a37d08 A |
351 | kern_return_t result = thread_suspend(platformThread); |
352 | return result == KERN_SUCCESS; | |
14957cd0 | 353 | #elif OS(WINDOWS) |
ed1e77d3 | 354 | bool threadIsSuspended = (SuspendThread(platformThreadHandle) != (DWORD)-1); |
40a37d08 A |
355 | ASSERT(threadIsSuspended); |
356 | return threadIsSuspended; | |
14957cd0 A |
357 | #elif USE(PTHREADS) |
358 | pthread_kill(platformThread, SigThreadSuspendResume); | |
40a37d08 | 359 | return true; |
14957cd0 A |
360 | #else |
361 | #error Need a way to suspend threads on this platform | |
362 | #endif | |
363 | } | |
364 | ||
ed1e77d3 | 365 | inline void MachineThreads::Thread::resume() |
14957cd0 A |
366 | { |
367 | #if OS(DARWIN) | |
368 | thread_resume(platformThread); | |
369 | #elif OS(WINDOWS) | |
ed1e77d3 | 370 | ResumeThread(platformThreadHandle); |
14957cd0 A |
371 | #elif USE(PTHREADS) |
372 | pthread_kill(platformThread, SigThreadSuspendResume); | |
373 | #else | |
374 | #error Need a way to resume threads on this platform | |
375 | #endif | |
376 | } | |
377 | ||
ed1e77d3 | 378 | size_t MachineThreads::Thread::getRegisters(MachineThreads::Thread::Registers& registers) |
14957cd0 | 379 | { |
ed1e77d3 | 380 | Thread::Registers::PlatformRegisters& regs = registers.regs; |
14957cd0 | 381 | #if OS(DARWIN) |
14957cd0 A |
382 | #if CPU(X86) |
383 | unsigned user_count = sizeof(regs)/sizeof(int); | |
384 | thread_state_flavor_t flavor = i386_THREAD_STATE; | |
385 | #elif CPU(X86_64) | |
386 | unsigned user_count = x86_THREAD_STATE64_COUNT; | |
387 | thread_state_flavor_t flavor = x86_THREAD_STATE64; | |
388 | #elif CPU(PPC) | |
389 | unsigned user_count = PPC_THREAD_STATE_COUNT; | |
390 | thread_state_flavor_t flavor = PPC_THREAD_STATE; | |
391 | #elif CPU(PPC64) | |
392 | unsigned user_count = PPC_THREAD_STATE64_COUNT; | |
393 | thread_state_flavor_t flavor = PPC_THREAD_STATE64; | |
394 | #elif CPU(ARM) | |
395 | unsigned user_count = ARM_THREAD_STATE_COUNT; | |
396 | thread_state_flavor_t flavor = ARM_THREAD_STATE; | |
93a37866 A |
397 | #elif CPU(ARM64) |
398 | unsigned user_count = ARM_THREAD_STATE64_COUNT; | |
399 | thread_state_flavor_t flavor = ARM_THREAD_STATE64; | |
14957cd0 A |
400 | #else |
401 | #error Unknown Architecture | |
402 | #endif | |
403 | ||
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); | |
408 | CRASH(); | |
409 | } | |
ed1e77d3 | 410 | return user_count * sizeof(uintptr_t); |
14957cd0 A |
411 | // end OS(DARWIN) |
412 | ||
413 | #elif OS(WINDOWS) | |
6fe7ccc8 | 414 | regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; |
ed1e77d3 | 415 | GetThreadContext(platformThreadHandle, ®s); |
14957cd0 A |
416 | return sizeof(CONTEXT); |
417 | #elif USE(PTHREADS) | |
418 | pthread_attr_init(®s); | |
419 | #if HAVE(PTHREAD_NP_H) || OS(NETBSD) | |
81345200 | 420 | #if !OS(OPENBSD) |
14957cd0 A |
421 | // e.g. on FreeBSD 5.4, neundorf@kde.org |
422 | pthread_attr_get_np(platformThread, ®s); | |
81345200 | 423 | #endif |
14957cd0 A |
424 | #else |
425 | // FIXME: this function is non-portable; other POSIX systems may have different np alternatives | |
426 | pthread_getattr_np(platformThread, ®s); | |
427 | #endif | |
428 | return 0; | |
429 | #else | |
430 | #error Need a way to get thread registers on this platform | |
431 | #endif | |
432 | } | |
433 | ||
ed1e77d3 | 434 | inline void* MachineThreads::Thread::Registers::stackPointer() const |
14957cd0 A |
435 | { |
436 | #if OS(DARWIN) | |
437 | ||
438 | #if __DARWIN_UNIX03 | |
439 | ||
440 | #if CPU(X86) | |
441 | return reinterpret_cast<void*>(regs.__esp); | |
442 | #elif CPU(X86_64) | |
443 | return reinterpret_cast<void*>(regs.__rsp); | |
444 | #elif CPU(PPC) || CPU(PPC64) | |
445 | return reinterpret_cast<void*>(regs.__r1); | |
446 | #elif CPU(ARM) | |
447 | return reinterpret_cast<void*>(regs.__sp); | |
93a37866 A |
448 | #elif CPU(ARM64) |
449 | return reinterpret_cast<void*>(regs.__sp); | |
14957cd0 A |
450 | #else |
451 | #error Unknown Architecture | |
452 | #endif | |
453 | ||
454 | #else // !__DARWIN_UNIX03 | |
455 | ||
456 | #if CPU(X86) | |
457 | return reinterpret_cast<void*>(regs.esp); | |
458 | #elif CPU(X86_64) | |
459 | return reinterpret_cast<void*>(regs.rsp); | |
460 | #elif CPU(PPC) || CPU(PPC64) | |
461 | return reinterpret_cast<void*>(regs.r1); | |
462 | #else | |
463 | #error Unknown Architecture | |
464 | #endif | |
465 | ||
466 | #endif // __DARWIN_UNIX03 | |
467 | ||
468 | // end OS(DARWIN) | |
6fe7ccc8 A |
469 | #elif OS(WINDOWS) |
470 | ||
471 | #if CPU(ARM) | |
472 | return reinterpret_cast<void*>((uintptr_t) regs.Sp); | |
473 | #elif CPU(MIPS) | |
474 | return reinterpret_cast<void*>((uintptr_t) regs.IntSp); | |
475 | #elif CPU(X86) | |
14957cd0 | 476 | return reinterpret_cast<void*>((uintptr_t) regs.Esp); |
6fe7ccc8 | 477 | #elif CPU(X86_64) |
14957cd0 | 478 | return reinterpret_cast<void*>((uintptr_t) regs.Rsp); |
6fe7ccc8 A |
479 | #else |
480 | #error Unknown Architecture | |
481 | #endif | |
482 | ||
14957cd0 A |
483 | #elif USE(PTHREADS) |
484 | void* stackBase = 0; | |
485 | size_t stackSize = 0; | |
81345200 A |
486 | #if OS(OPENBSD) |
487 | stack_t ss; | |
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; | |
491 | #else | |
14957cd0 | 492 | int rc = pthread_attr_getstack(®s, &stackBase, &stackSize); |
81345200 | 493 | #endif |
14957cd0 A |
494 | (void)rc; // FIXME: Deal with error code somehow? Seems fatal. |
495 | ASSERT(stackBase); | |
496 | return static_cast<char*>(stackBase) + stackSize; | |
497 | #else | |
498 | #error Need a way to get the stack pointer for another thread on this platform | |
499 | #endif | |
500 | } | |
501 | ||
ed1e77d3 | 502 | void MachineThreads::Thread::freeRegisters(MachineThreads::Thread::Registers& registers) |
14957cd0 | 503 | { |
ed1e77d3 | 504 | Thread::Registers::PlatformRegisters& regs = registers.regs; |
81345200 | 505 | #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) |
14957cd0 A |
506 | pthread_attr_destroy(®s); |
507 | #else | |
508 | UNUSED_PARAM(regs); | |
509 | #endif | |
510 | } | |
511 | ||
ed1e77d3 | 512 | std::pair<void*, size_t> MachineThreads::Thread::captureStack(void* stackTop) |
14957cd0 | 513 | { |
ed1e77d3 A |
514 | void* begin = stackBase; |
515 | void* end = reinterpret_cast<void*>( | |
516 | WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackTop))); | |
517 | if (begin > end) | |
518 | std::swap(begin, end); | |
519 | return std::make_pair(begin, static_cast<char*>(end) - static_cast<char*>(begin)); | |
520 | } | |
14957cd0 | 521 | |
ed1e77d3 A |
522 | static void copyMemory(void* dst, const void* src, size_t size) |
523 | { | |
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)); | |
529 | ||
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); | |
533 | while (size--) | |
534 | *dstPtr++ = *srcPtr++; | |
535 | } | |
536 | ||
537 | ||
538 | ||
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) | |
549 | { | |
550 | Thread::Registers registers; | |
551 | size_t registersSize = thread->getRegisters(registers); | |
552 | std::pair<void*, size_t> stack = thread->captureStack(registers.stackPointer()); | |
14957cd0 | 553 | |
ed1e77d3 | 554 | bool canCopy = *size + registersSize + stack.second <= capacity; |
14957cd0 | 555 | |
ed1e77d3 A |
556 | if (canCopy) |
557 | copyMemory(static_cast<char*>(buffer) + *size, ®isters, registersSize); | |
558 | *size += registersSize; | |
14957cd0 | 559 | |
ed1e77d3 A |
560 | if (canCopy) |
561 | copyMemory(static_cast<char*>(buffer) + *size, stack.first, stack.second); | |
562 | *size += stack.second; | |
14957cd0 | 563 | |
ed1e77d3 A |
564 | thread->freeRegisters(registers); |
565 | } | |
14957cd0 | 566 | |
ed1e77d3 A |
567 | bool MachineThreads::tryCopyOtherThreadStacks(MutexLocker&, void* buffer, size_t capacity, size_t* size) |
568 | { | |
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); | |
14957cd0 | 573 | |
ed1e77d3 | 574 | *size = 0; |
40a37d08 | 575 | |
ed1e77d3 A |
576 | PlatformThread currentPlatformThread = getCurrentPlatformThread(); |
577 | int numberOfThreads = 0; // Using 0 to denote that we haven't counted the number of threads yet. | |
578 | int index = 1; | |
579 | Thread* threadsToBeDeleted = nullptr; | |
580 | ||
581 | Thread* previousThread = nullptr; | |
582 | for (Thread* thread = m_registeredThreads; thread; index++) { | |
583 | if (*thread != currentPlatformThread) { | |
584 | bool success = thread->suspend(); | |
40a37d08 | 585 | #if OS(DARWIN) |
ed1e77d3 A |
586 | if (!success) { |
587 | if (!numberOfThreads) { | |
588 | for (Thread* countedThread = m_registeredThreads; countedThread; countedThread = countedThread->next) | |
589 | numberOfThreads++; | |
40a37d08 | 590 | } |
ed1e77d3 A |
591 | |
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); | |
595 | ||
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)); | |
599 | ||
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; | |
608 | ||
609 | if (previousThread) | |
610 | previousThread->next = nextThread; | |
611 | else | |
612 | m_registeredThreads = nextThread; | |
613 | thread = nextThread; | |
614 | continue; | |
615 | } | |
40a37d08 | 616 | #else |
ed1e77d3 A |
617 | UNUSED_PARAM(numberOfThreads); |
618 | UNUSED_PARAM(previousThread); | |
619 | ASSERT_UNUSED(success, success); | |
40a37d08 | 620 | #endif |
93a37866 | 621 | } |
ed1e77d3 A |
622 | previousThread = thread; |
623 | thread = thread->next; | |
624 | } | |
93a37866 | 625 | |
ed1e77d3 A |
626 | for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { |
627 | if (*thread != currentPlatformThread) | |
628 | tryCopyOtherThreadStack(thread, buffer, capacity, size); | |
629 | } | |
93a37866 | 630 | |
ed1e77d3 A |
631 | for (Thread* thread = m_registeredThreads; thread; thread = thread->next) { |
632 | if (*thread != currentPlatformThread) | |
633 | thread->resume(); | |
634 | } | |
93a37866 | 635 | |
ed1e77d3 A |
636 | for (Thread* thread = threadsToBeDeleted; thread; ) { |
637 | Thread* nextThread = thread->next; | |
638 | delete thread; | |
639 | thread = nextThread; | |
14957cd0 | 640 | } |
ed1e77d3 A |
641 | |
642 | return *size <= capacity; | |
643 | } | |
644 | ||
645 | static void growBuffer(size_t size, void** buffer, size_t* capacity) | |
646 | { | |
647 | if (*buffer) | |
648 | fastFree(*buffer); | |
649 | ||
650 | *capacity = WTF::roundUpToMultipleOf(WTF::pageSize(), size * 2); | |
651 | *buffer = fastMalloc(*capacity); | |
652 | } | |
653 | ||
654 | void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackOrigin, void* stackTop, RegisterState& calleeSavedRegisters) | |
655 | { | |
656 | gatherFromCurrentThread(conservativeRoots, jitStubRoutines, codeBlocks, stackOrigin, stackTop, calleeSavedRegisters); | |
657 | ||
658 | size_t size; | |
659 | size_t capacity = 0; | |
660 | void* buffer = nullptr; | |
661 | MutexLocker lock(m_registeredThreadsMutex); | |
662 | while (!tryCopyOtherThreadStacks(lock, buffer, capacity, &size)) | |
663 | growBuffer(size, &buffer, &capacity); | |
664 | ||
665 | if (!buffer) | |
666 | return; | |
667 | ||
668 | conservativeRoots.add(buffer, static_cast<char*>(buffer) + size, jitStubRoutines, codeBlocks); | |
669 | fastFree(buffer); | |
14957cd0 A |
670 | } |
671 | ||
672 | } // namespace JSC |