]> git.saurik.com Git - apple/javascriptcore.git/blob - heap/MachineStackMarker.cpp
JavaScriptCore-7600.1.4.16.1.tar.gz
[apple/javascriptcore.git] / heap / MachineStackMarker.cpp
1 /*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
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"
28 #include "JSCInlines.h"
29 #include "VM.h"
30 #include <setjmp.h>
31 #include <stdlib.h>
32 #include <wtf/StdLibExtras.h>
33
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
47 #elif OS(UNIX)
48
49 #include <sys/mman.h>
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
62 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
63 #include <signal.h>
64 #endif
65
66 #endif
67
68 using namespace WTF;
69
70 namespace JSC {
71
72 static inline void swapIfBackwards(void*& begin, void*& end)
73 {
74 #if OS(WINCE)
75 if (begin <= end)
76 return;
77 std::swap(begin, end);
78 #else
79 UNUSED_PARAM(begin);
80 UNUSED_PARAM(end);
81 #endif
82 }
83
84 #if OS(DARWIN)
85 typedef mach_port_t PlatformThread;
86 #elif OS(WINDOWS)
87 typedef HANDLE PlatformThread;
88 #elif USE(PTHREADS)
89 typedef pthread_t PlatformThread;
90 static const int SigThreadSuspendResume = SIGUSR2;
91
92 #if defined(SA_RESTART)
93 static void pthreadSignalHandlerSuspendResume(int)
94 {
95 sigset_t signalSet;
96 sigemptyset(&signalSet);
97 sigaddset(&signalSet, SigThreadSuspendResume);
98 sigsuspend(&signalSet);
99 }
100 #endif
101 #endif
102
103 class MachineThreads::Thread {
104 WTF_MAKE_FAST_ALLOCATED;
105 public:
106 Thread(const PlatformThread& platThread, void* base)
107 : platformThread(platThread)
108 , stackBase(base)
109 {
110 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART)
111 // if we have SA_RESTART, enable SIGUSR2 debugging mechanism
112 struct sigaction action;
113 action.sa_handler = pthreadSignalHandlerSuspendResume;
114 sigemptyset(&action.sa_mask);
115 action.sa_flags = SA_RESTART;
116 sigaction(SigThreadSuspendResume, &action, 0);
117
118 sigset_t mask;
119 sigemptyset(&mask);
120 sigaddset(&mask, SigThreadSuspendResume);
121 pthread_sigmask(SIG_UNBLOCK, &mask, 0);
122 #endif
123 }
124
125 Thread* next;
126 PlatformThread platformThread;
127 void* stackBase;
128 };
129
130 MachineThreads::MachineThreads(Heap* heap)
131 : m_registeredThreads(0)
132 , m_threadSpecific(0)
133 #if !ASSERT_DISABLED
134 , m_heap(heap)
135 #endif
136 {
137 UNUSED_PARAM(heap);
138 }
139
140 MachineThreads::~MachineThreads()
141 {
142 if (m_threadSpecific)
143 threadSpecificKeyDelete(m_threadSpecific);
144
145 MutexLocker registeredThreadsLock(m_registeredThreadsMutex);
146 for (Thread* t = m_registeredThreads; t;) {
147 Thread* next = t->next;
148 delete t;
149 t = next;
150 }
151 }
152
153 static inline PlatformThread getCurrentPlatformThread()
154 {
155 #if OS(DARWIN)
156 return pthread_mach_thread_np(pthread_self());
157 #elif OS(WINDOWS)
158 return GetCurrentThread();
159 #elif USE(PTHREADS)
160 return pthread_self();
161 #endif
162 }
163
164 static inline bool equalThread(const PlatformThread& first, const PlatformThread& second)
165 {
166 #if OS(DARWIN) || OS(WINDOWS)
167 return first == second;
168 #elif USE(PTHREADS)
169 return !!pthread_equal(first, second);
170 #else
171 #error Need a way to compare threads on this platform
172 #endif
173 }
174
175 void MachineThreads::makeUsableFromMultipleThreads()
176 {
177 if (m_threadSpecific)
178 return;
179
180 threadSpecificKeyCreate(&m_threadSpecific, removeThread);
181 }
182
183 void MachineThreads::addCurrentThread()
184 {
185 ASSERT(!m_heap->vm()->hasExclusiveThread() || m_heap->vm()->exclusiveThread() == std::this_thread::get_id());
186
187 if (!m_threadSpecific || threadSpecificGet(m_threadSpecific))
188 return;
189
190 threadSpecificSet(m_threadSpecific, this);
191 Thread* thread = new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin());
192
193 MutexLocker lock(m_registeredThreadsMutex);
194
195 thread->next = m_registeredThreads;
196 m_registeredThreads = thread;
197 }
198
199 void MachineThreads::removeThread(void* p)
200 {
201 if (p)
202 static_cast<MachineThreads*>(p)->removeCurrentThread();
203 }
204
205 void MachineThreads::removeCurrentThread()
206 {
207 PlatformThread currentPlatformThread = getCurrentPlatformThread();
208
209 MutexLocker lock(m_registeredThreadsMutex);
210
211 if (equalThread(currentPlatformThread, m_registeredThreads->platformThread)) {
212 Thread* t = m_registeredThreads;
213 m_registeredThreads = m_registeredThreads->next;
214 delete t;
215 } else {
216 Thread* last = m_registeredThreads;
217 Thread* t;
218 for (t = m_registeredThreads->next; t; t = t->next) {
219 if (equalThread(t->platformThread, currentPlatformThread)) {
220 last->next = t->next;
221 break;
222 }
223 last = t;
224 }
225 ASSERT(t); // If t is NULL, we never found ourselves in the list.
226 delete t;
227 }
228 }
229
230 void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent, RegisterState& registers)
231 {
232 void* registersBegin = &registers;
233 void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(&registers + 1)));
234 swapIfBackwards(registersBegin, registersEnd);
235 conservativeRoots.add(registersBegin, registersEnd, jitStubRoutines, codeBlocks);
236
237 void* stackBegin = stackCurrent;
238 void* stackEnd = wtfThreadData().stack().origin();
239 swapIfBackwards(stackBegin, stackEnd);
240 conservativeRoots.add(stackBegin, stackEnd, jitStubRoutines, codeBlocks);
241 }
242
243 static inline bool suspendThread(const PlatformThread& platformThread)
244 {
245 #if OS(DARWIN)
246 kern_return_t result = thread_suspend(platformThread);
247 return result == KERN_SUCCESS;
248 #elif OS(WINDOWS)
249 bool threadIsSuspended = (SuspendThread(platformThread) != (DWORD)-1);
250 ASSERT(threadIsSuspended);
251 return threadIsSuspended;
252 #elif USE(PTHREADS)
253 pthread_kill(platformThread, SigThreadSuspendResume);
254 return true;
255 #else
256 #error Need a way to suspend threads on this platform
257 #endif
258 }
259
260 static inline void resumeThread(const PlatformThread& platformThread)
261 {
262 #if OS(DARWIN)
263 thread_resume(platformThread);
264 #elif OS(WINDOWS)
265 ResumeThread(platformThread);
266 #elif USE(PTHREADS)
267 pthread_kill(platformThread, SigThreadSuspendResume);
268 #else
269 #error Need a way to resume threads on this platform
270 #endif
271 }
272
273 typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
274
275 #if OS(DARWIN)
276
277 #if CPU(X86)
278 typedef i386_thread_state_t PlatformThreadRegisters;
279 #elif CPU(X86_64)
280 typedef x86_thread_state64_t PlatformThreadRegisters;
281 #elif CPU(PPC)
282 typedef ppc_thread_state_t PlatformThreadRegisters;
283 #elif CPU(PPC64)
284 typedef ppc_thread_state64_t PlatformThreadRegisters;
285 #elif CPU(ARM)
286 typedef arm_thread_state_t PlatformThreadRegisters;
287 #elif CPU(ARM64)
288 typedef arm_thread_state64_t PlatformThreadRegisters;
289 #else
290 #error Unknown Architecture
291 #endif
292
293 #elif OS(WINDOWS)
294 typedef CONTEXT PlatformThreadRegisters;
295 #elif USE(PTHREADS)
296 typedef pthread_attr_t PlatformThreadRegisters;
297 #else
298 #error Need a thread register struct for this platform
299 #endif
300
301 static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs)
302 {
303 #if OS(DARWIN)
304
305 #if CPU(X86)
306 unsigned user_count = sizeof(regs)/sizeof(int);
307 thread_state_flavor_t flavor = i386_THREAD_STATE;
308 #elif CPU(X86_64)
309 unsigned user_count = x86_THREAD_STATE64_COUNT;
310 thread_state_flavor_t flavor = x86_THREAD_STATE64;
311 #elif CPU(PPC)
312 unsigned user_count = PPC_THREAD_STATE_COUNT;
313 thread_state_flavor_t flavor = PPC_THREAD_STATE;
314 #elif CPU(PPC64)
315 unsigned user_count = PPC_THREAD_STATE64_COUNT;
316 thread_state_flavor_t flavor = PPC_THREAD_STATE64;
317 #elif CPU(ARM)
318 unsigned user_count = ARM_THREAD_STATE_COUNT;
319 thread_state_flavor_t flavor = ARM_THREAD_STATE;
320 #elif CPU(ARM64)
321 unsigned user_count = ARM_THREAD_STATE64_COUNT;
322 thread_state_flavor_t flavor = ARM_THREAD_STATE64;
323 #else
324 #error Unknown Architecture
325 #endif
326
327 kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)&regs, &user_count);
328 if (result != KERN_SUCCESS) {
329 WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
330 "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);
331 CRASH();
332 }
333 return user_count * sizeof(usword_t);
334 // end OS(DARWIN)
335
336 #elif OS(WINDOWS)
337 regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
338 GetThreadContext(platformThread, &regs);
339 return sizeof(CONTEXT);
340 #elif USE(PTHREADS)
341 pthread_attr_init(&regs);
342 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
343 #if !OS(OPENBSD)
344 // e.g. on FreeBSD 5.4, neundorf@kde.org
345 pthread_attr_get_np(platformThread, &regs);
346 #endif
347 #else
348 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
349 pthread_getattr_np(platformThread, &regs);
350 #endif
351 return 0;
352 #else
353 #error Need a way to get thread registers on this platform
354 #endif
355 }
356
357 static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
358 {
359 #if OS(DARWIN)
360
361 #if __DARWIN_UNIX03
362
363 #if CPU(X86)
364 return reinterpret_cast<void*>(regs.__esp);
365 #elif CPU(X86_64)
366 return reinterpret_cast<void*>(regs.__rsp);
367 #elif CPU(PPC) || CPU(PPC64)
368 return reinterpret_cast<void*>(regs.__r1);
369 #elif CPU(ARM)
370 return reinterpret_cast<void*>(regs.__sp);
371 #elif CPU(ARM64)
372 return reinterpret_cast<void*>(regs.__sp);
373 #else
374 #error Unknown Architecture
375 #endif
376
377 #else // !__DARWIN_UNIX03
378
379 #if CPU(X86)
380 return reinterpret_cast<void*>(regs.esp);
381 #elif CPU(X86_64)
382 return reinterpret_cast<void*>(regs.rsp);
383 #elif CPU(PPC) || CPU(PPC64)
384 return reinterpret_cast<void*>(regs.r1);
385 #else
386 #error Unknown Architecture
387 #endif
388
389 #endif // __DARWIN_UNIX03
390
391 // end OS(DARWIN)
392 #elif OS(WINDOWS)
393
394 #if CPU(ARM)
395 return reinterpret_cast<void*>((uintptr_t) regs.Sp);
396 #elif CPU(MIPS)
397 return reinterpret_cast<void*>((uintptr_t) regs.IntSp);
398 #elif CPU(X86)
399 return reinterpret_cast<void*>((uintptr_t) regs.Esp);
400 #elif CPU(X86_64)
401 return reinterpret_cast<void*>((uintptr_t) regs.Rsp);
402 #else
403 #error Unknown Architecture
404 #endif
405
406 #elif USE(PTHREADS)
407 void* stackBase = 0;
408 size_t stackSize = 0;
409 #if OS(OPENBSD)
410 stack_t ss;
411 int rc = pthread_stackseg_np(pthread_self(), &ss);
412 stackBase = (void*)((size_t) ss.ss_sp - ss.ss_size);
413 stackSize = ss.ss_size;
414 #else
415 int rc = pthread_attr_getstack(&regs, &stackBase, &stackSize);
416 #endif
417 (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
418 ASSERT(stackBase);
419 return static_cast<char*>(stackBase) + stackSize;
420 #else
421 #error Need a way to get the stack pointer for another thread on this platform
422 #endif
423 }
424
425 static void freePlatformThreadRegisters(PlatformThreadRegisters& regs)
426 {
427 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
428 pthread_attr_destroy(&regs);
429 #else
430 UNUSED_PARAM(regs);
431 #endif
432 }
433
434 void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks)
435 {
436 PlatformThreadRegisters regs;
437 size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
438
439 conservativeRoots.add(static_cast<void*>(&regs), static_cast<void*>(reinterpret_cast<char*>(&regs) + regSize), jitStubRoutines, codeBlocks);
440
441 void* stackPointer = otherThreadStackPointer(regs);
442 void* stackBase = thread->stackBase;
443 swapIfBackwards(stackPointer, stackBase);
444 stackPointer = reinterpret_cast<void*>(WTF::roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(stackPointer)));
445 conservativeRoots.add(stackPointer, stackBase, jitStubRoutines, codeBlocks);
446
447 freePlatformThreadRegisters(regs);
448 }
449
450 void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, JITStubRoutineSet& jitStubRoutines, CodeBlockSet& codeBlocks, void* stackCurrent, RegisterState& registers)
451 {
452 gatherFromCurrentThread(conservativeRoots, jitStubRoutines, codeBlocks, stackCurrent, registers);
453
454 if (m_threadSpecific) {
455 PlatformThread currentPlatformThread = getCurrentPlatformThread();
456
457 MutexLocker lock(m_registeredThreadsMutex);
458
459 Thread* threadsToBeDeleted = nullptr;
460
461 #ifndef NDEBUG
462 // Forbid malloc during the gather phase. The gather phase suspends
463 // threads, so a malloc during gather would risk a deadlock with a
464 // thread that had been suspended while holding the malloc lock.
465 fastMallocForbid();
466 #endif
467 int numberOfThreads = 0; // Using 0 to denote that we haven't counted the number of threads yet.
468 int index = 1;
469 Thread* previousThread = nullptr;
470 for (Thread* thread = m_registeredThreads; thread; index++) {
471 if (!equalThread(thread->platformThread, currentPlatformThread)) {
472 bool success = suspendThread(thread->platformThread);
473 #if OS(DARWIN)
474 if (!success) {
475 if (!numberOfThreads) {
476 for (Thread* countedThread = m_registeredThreads; countedThread; countedThread = countedThread->next)
477 numberOfThreads++;
478 }
479
480 // Re-do the suspension to get the actual failure result for logging.
481 kern_return_t error = thread_suspend(thread->platformThread);
482 ASSERT(error != KERN_SUCCESS);
483
484 WTFReportError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
485 "JavaScript garbage collection encountered an invalid thread (err 0x%x): Thread [%d/%d: %p] platformThread %p.",
486 error, index, numberOfThreads, thread, reinterpret_cast<void*>(thread->platformThread));
487
488 // Put the invalid thread on the threadsToBeDeleted list.
489 // We can't just delete it here because we have suspended other
490 // threads, and they may still be holding the C heap lock which
491 // we need for deleting the invalid thread. Hence, we need to
492 // defer the deletion till after we have resumed all threads.
493 Thread* nextThread = thread->next;
494 thread->next = threadsToBeDeleted;
495 threadsToBeDeleted = thread;
496
497 if (previousThread)
498 previousThread->next = nextThread;
499 else
500 m_registeredThreads = nextThread;
501 thread = nextThread;
502 continue;
503 }
504 #else
505 UNUSED_PARAM(numberOfThreads);
506 ASSERT_UNUSED(success, success);
507 #endif
508 }
509 previousThread = thread;
510 thread = thread->next;
511 }
512
513 // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
514 // and since this is a shared heap, they are real locks.
515 for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
516 if (!equalThread(thread->platformThread, currentPlatformThread))
517 gatherFromOtherThread(conservativeRoots, thread, jitStubRoutines, codeBlocks);
518 }
519
520 for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
521 if (!equalThread(thread->platformThread, currentPlatformThread))
522 resumeThread(thread->platformThread);
523 }
524
525 #ifndef NDEBUG
526 fastMallocAllow();
527 #endif
528 for (Thread* thread = threadsToBeDeleted; thread; ) {
529 Thread* nextThread = thread->next;
530 delete thread;
531 thread = nextThread;
532 }
533 }
534 }
535
536 } // namespace JSC