]> git.saurik.com Git - apple/javascriptcore.git/blame - heap/MachineStackMarker.cpp
JavaScriptCore-1097.3.3.tar.gz
[apple/javascriptcore.git] / heap / MachineStackMarker.cpp
CommitLineData
14957cd0
A
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 "JSGlobalData.h"
29#include <setjmp.h>
30#include <stdlib.h>
31#include <wtf/StdLibExtras.h>
32
14957cd0
A
33#if OS(DARWIN)
34
35#include <mach/mach_init.h>
36#include <mach/mach_port.h>
37#include <mach/task.h>
38#include <mach/thread_act.h>
39#include <mach/vm_map.h>
40
41#elif OS(WINDOWS)
42
43#include <windows.h>
44#include <malloc.h>
45
14957cd0
A
46#elif OS(UNIX)
47
48#include <stdlib.h>
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
62#if OS(QNX)
63#include <fcntl.h>
64#include <sys/procfs.h>
65#include <stdio.h>
66#include <errno.h>
67#endif
68
69#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN)
70#include <signal.h>
14957cd0
A
71#endif
72
73#endif
74
75using namespace WTF;
76
77namespace JSC {
78
79static inline void swapIfBackwards(void*& begin, void*& end)
80{
81#if OS(WINCE)
82 if (begin <= end)
83 return;
84 std::swap(begin, end);
85#else
86UNUSED_PARAM(begin);
87UNUSED_PARAM(end);
88#endif
89}
90
14957cd0
A
91#if OS(DARWIN)
92typedef mach_port_t PlatformThread;
93#elif OS(WINDOWS)
94typedef HANDLE PlatformThread;
95#elif USE(PTHREADS)
96typedef pthread_t PlatformThread;
97static const int SigThreadSuspendResume = SIGUSR2;
98
6fe7ccc8 99#if defined(SA_RESTART)
14957cd0
A
100static void pthreadSignalHandlerSuspendResume(int signo)
101{
102 sigset_t signalSet;
103 sigemptyset(&signalSet);
104 sigaddset(&signalSet, SigThreadSuspendResume);
105 sigsuspend(&signalSet);
106}
107#endif
6fe7ccc8 108#endif
14957cd0
A
109
110class MachineThreads::Thread {
111public:
6fe7ccc8
A
112 Thread(const PlatformThread& platThread, void* base)
113 : platformThread(platThread)
14957cd0
A
114 , stackBase(base)
115 {
6fe7ccc8
A
116#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART)
117 // if we have SA_RESTART, enable SIGUSR2 debugging mechanism
14957cd0
A
118 struct sigaction action;
119 action.sa_handler = pthreadSignalHandlerSuspendResume;
120 sigemptyset(&action.sa_mask);
121 action.sa_flags = SA_RESTART;
122 sigaction(SigThreadSuspendResume, &action, 0);
123
124 sigset_t mask;
125 sigemptyset(&mask);
126 sigaddset(&mask, SigThreadSuspendResume);
127 pthread_sigmask(SIG_UNBLOCK, &mask, 0);
128#endif
129 }
130
131 Thread* next;
14957cd0
A
132 PlatformThread platformThread;
133 void* stackBase;
134};
135
14957cd0
A
136MachineThreads::MachineThreads(Heap* heap)
137 : m_heap(heap)
14957cd0
A
138 , m_registeredThreads(0)
139 , m_threadSpecific(0)
14957cd0
A
140{
141}
142
143MachineThreads::~MachineThreads()
144{
14957cd0
A
145 if (m_threadSpecific) {
146 int error = pthread_key_delete(m_threadSpecific);
147 ASSERT_UNUSED(error, !error);
148 }
149
150 MutexLocker registeredThreadsLock(m_registeredThreadsMutex);
151 for (Thread* t = m_registeredThreads; t;) {
152 Thread* next = t->next;
153 delete t;
154 t = next;
155 }
14957cd0
A
156}
157
14957cd0
A
158static inline PlatformThread getCurrentPlatformThread()
159{
160#if OS(DARWIN)
161 return pthread_mach_thread_np(pthread_self());
162#elif OS(WINDOWS)
6fe7ccc8 163 return GetCurrentThread();
14957cd0
A
164#elif USE(PTHREADS)
165 return pthread_self();
166#endif
167}
168
6fe7ccc8
A
169static inline bool equalThread(const PlatformThread& first, const PlatformThread& second)
170{
171#if OS(DARWIN) || OS(WINDOWS)
172 return first == second;
173#elif USE(PTHREADS)
174 return !!pthread_equal(first, second);
175#else
176#error Need a way to compare threads on this platform
177#endif
178}
179
14957cd0
A
180void MachineThreads::makeUsableFromMultipleThreads()
181{
182 if (m_threadSpecific)
183 return;
184
185 int error = pthread_key_create(&m_threadSpecific, removeThread);
186 if (error)
187 CRASH();
188}
189
190void MachineThreads::addCurrentThread()
191{
192 ASSERT(!m_heap->globalData()->exclusiveThread || m_heap->globalData()->exclusiveThread == currentThread());
193
194 if (!m_threadSpecific || pthread_getspecific(m_threadSpecific))
195 return;
196
197 pthread_setspecific(m_threadSpecific, this);
6fe7ccc8 198 Thread* thread = new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin());
14957cd0
A
199
200 MutexLocker lock(m_registeredThreadsMutex);
201
202 thread->next = m_registeredThreads;
203 m_registeredThreads = thread;
204}
205
206void MachineThreads::removeThread(void* p)
207{
208 if (p)
209 static_cast<MachineThreads*>(p)->removeCurrentThread();
210}
211
212void MachineThreads::removeCurrentThread()
213{
6fe7ccc8 214 PlatformThread currentPlatformThread = getCurrentPlatformThread();
14957cd0
A
215
216 MutexLocker lock(m_registeredThreadsMutex);
217
6fe7ccc8 218 if (equalThread(currentPlatformThread, m_registeredThreads->platformThread)) {
14957cd0
A
219 Thread* t = m_registeredThreads;
220 m_registeredThreads = m_registeredThreads->next;
221 delete t;
222 } else {
223 Thread* last = m_registeredThreads;
224 Thread* t;
225 for (t = m_registeredThreads->next; t; t = t->next) {
6fe7ccc8 226 if (equalThread(t->platformThread, currentPlatformThread)) {
14957cd0
A
227 last->next = t->next;
228 break;
229 }
230 last = t;
231 }
232 ASSERT(t); // If t is NULL, we never found ourselves in the list.
233 delete t;
234 }
235}
236
14957cd0
A
237#if COMPILER(GCC)
238#define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*))))
239#else
240#define REGISTER_BUFFER_ALIGNMENT
241#endif
242
243void MachineThreads::gatherFromCurrentThread(ConservativeRoots& conservativeRoots, void* stackCurrent)
244{
245 // setjmp forces volatile registers onto the stack
246 jmp_buf registers REGISTER_BUFFER_ALIGNMENT;
247#if COMPILER(MSVC)
248#pragma warning(push)
249#pragma warning(disable: 4611)
250#endif
251 setjmp(registers);
252#if COMPILER(MSVC)
253#pragma warning(pop)
254#endif
255
256 void* registersBegin = &registers;
257 void* registersEnd = reinterpret_cast<void*>(roundUpToMultipleOf<sizeof(void*)>(reinterpret_cast<uintptr_t>(&registers + 1)));
258 swapIfBackwards(registersBegin, registersEnd);
259 conservativeRoots.add(registersBegin, registersEnd);
260
261 void* stackBegin = stackCurrent;
6fe7ccc8 262 void* stackEnd = wtfThreadData().stack().origin();
14957cd0
A
263 swapIfBackwards(stackBegin, stackEnd);
264 conservativeRoots.add(stackBegin, stackEnd);
265}
266
14957cd0
A
267static inline void suspendThread(const PlatformThread& platformThread)
268{
269#if OS(DARWIN)
270 thread_suspend(platformThread);
271#elif OS(WINDOWS)
272 SuspendThread(platformThread);
273#elif USE(PTHREADS)
274 pthread_kill(platformThread, SigThreadSuspendResume);
275#else
276#error Need a way to suspend threads on this platform
277#endif
278}
279
280static inline void resumeThread(const PlatformThread& platformThread)
281{
282#if OS(DARWIN)
283 thread_resume(platformThread);
284#elif OS(WINDOWS)
285 ResumeThread(platformThread);
286#elif USE(PTHREADS)
287 pthread_kill(platformThread, SigThreadSuspendResume);
288#else
289#error Need a way to resume threads on this platform
290#endif
291}
292
293typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
294
295#if OS(DARWIN)
296
297#if CPU(X86)
298typedef i386_thread_state_t PlatformThreadRegisters;
299#elif CPU(X86_64)
300typedef x86_thread_state64_t PlatformThreadRegisters;
301#elif CPU(PPC)
302typedef ppc_thread_state_t PlatformThreadRegisters;
303#elif CPU(PPC64)
304typedef ppc_thread_state64_t PlatformThreadRegisters;
305#elif CPU(ARM)
306typedef arm_thread_state_t PlatformThreadRegisters;
307#else
308#error Unknown Architecture
309#endif
310
311#elif OS(WINDOWS)
312typedef CONTEXT PlatformThreadRegisters;
6fe7ccc8
A
313#elif OS(QNX)
314typedef struct _debug_thread_info PlatformThreadRegisters;
14957cd0
A
315#elif USE(PTHREADS)
316typedef pthread_attr_t PlatformThreadRegisters;
317#else
318#error Need a thread register struct for this platform
319#endif
320
321static size_t getPlatformThreadRegisters(const PlatformThread& platformThread, PlatformThreadRegisters& regs)
322{
323#if OS(DARWIN)
324
325#if CPU(X86)
326 unsigned user_count = sizeof(regs)/sizeof(int);
327 thread_state_flavor_t flavor = i386_THREAD_STATE;
328#elif CPU(X86_64)
329 unsigned user_count = x86_THREAD_STATE64_COUNT;
330 thread_state_flavor_t flavor = x86_THREAD_STATE64;
331#elif CPU(PPC)
332 unsigned user_count = PPC_THREAD_STATE_COUNT;
333 thread_state_flavor_t flavor = PPC_THREAD_STATE;
334#elif CPU(PPC64)
335 unsigned user_count = PPC_THREAD_STATE64_COUNT;
336 thread_state_flavor_t flavor = PPC_THREAD_STATE64;
337#elif CPU(ARM)
338 unsigned user_count = ARM_THREAD_STATE_COUNT;
339 thread_state_flavor_t flavor = ARM_THREAD_STATE;
340#else
341#error Unknown Architecture
342#endif
343
344 kern_return_t result = thread_get_state(platformThread, flavor, (thread_state_t)&regs, &user_count);
345 if (result != KERN_SUCCESS) {
346 WTFReportFatalError(__FILE__, __LINE__, WTF_PRETTY_FUNCTION,
347 "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);
348 CRASH();
349 }
350 return user_count * sizeof(usword_t);
351// end OS(DARWIN)
352
353#elif OS(WINDOWS)
6fe7ccc8 354 regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
14957cd0
A
355 GetThreadContext(platformThread, &regs);
356 return sizeof(CONTEXT);
6fe7ccc8
A
357#elif OS(QNX)
358 memset(&regs, 0, sizeof(regs));
359 regs.tid = pthread_self();
360 int fd = open("/proc/self", O_RDONLY);
361 if (fd == -1) {
362 LOG_ERROR("Unable to open /proc/self (errno: %d)", errno);
363 CRASH();
364 }
365 devctl(fd, DCMD_PROC_TIDSTATUS, &regs, sizeof(regs), 0);
366 close(fd);
14957cd0
A
367#elif USE(PTHREADS)
368 pthread_attr_init(&regs);
369#if HAVE(PTHREAD_NP_H) || OS(NETBSD)
370 // e.g. on FreeBSD 5.4, neundorf@kde.org
371 pthread_attr_get_np(platformThread, &regs);
372#else
373 // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
374 pthread_getattr_np(platformThread, &regs);
375#endif
376 return 0;
377#else
378#error Need a way to get thread registers on this platform
379#endif
380}
381
382static inline void* otherThreadStackPointer(const PlatformThreadRegisters& regs)
383{
384#if OS(DARWIN)
385
386#if __DARWIN_UNIX03
387
388#if CPU(X86)
389 return reinterpret_cast<void*>(regs.__esp);
390#elif CPU(X86_64)
391 return reinterpret_cast<void*>(regs.__rsp);
392#elif CPU(PPC) || CPU(PPC64)
393 return reinterpret_cast<void*>(regs.__r1);
394#elif CPU(ARM)
395 return reinterpret_cast<void*>(regs.__sp);
396#else
397#error Unknown Architecture
398#endif
399
400#else // !__DARWIN_UNIX03
401
402#if CPU(X86)
403 return reinterpret_cast<void*>(regs.esp);
404#elif CPU(X86_64)
405 return reinterpret_cast<void*>(regs.rsp);
406#elif CPU(PPC) || CPU(PPC64)
407 return reinterpret_cast<void*>(regs.r1);
408#else
409#error Unknown Architecture
410#endif
411
412#endif // __DARWIN_UNIX03
413
414// end OS(DARWIN)
6fe7ccc8
A
415#elif OS(WINDOWS)
416
417#if CPU(ARM)
418 return reinterpret_cast<void*>((uintptr_t) regs.Sp);
419#elif CPU(MIPS)
420 return reinterpret_cast<void*>((uintptr_t) regs.IntSp);
421#elif CPU(X86)
14957cd0 422 return reinterpret_cast<void*>((uintptr_t) regs.Esp);
6fe7ccc8 423#elif CPU(X86_64)
14957cd0 424 return reinterpret_cast<void*>((uintptr_t) regs.Rsp);
6fe7ccc8
A
425#else
426#error Unknown Architecture
427#endif
428
429#elif OS(QNX)
430 return reinterpret_cast<void*>((uintptr_t) regs.sp);
431
14957cd0
A
432#elif USE(PTHREADS)
433 void* stackBase = 0;
434 size_t stackSize = 0;
435 int rc = pthread_attr_getstack(&regs, &stackBase, &stackSize);
436 (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
437 ASSERT(stackBase);
438 return static_cast<char*>(stackBase) + stackSize;
439#else
440#error Need a way to get the stack pointer for another thread on this platform
441#endif
442}
443
444static void freePlatformThreadRegisters(PlatformThreadRegisters& regs)
445{
6fe7ccc8 446#if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && !OS(QNX)
14957cd0
A
447 pthread_attr_destroy(&regs);
448#else
449 UNUSED_PARAM(regs);
450#endif
451}
452
453void MachineThreads::gatherFromOtherThread(ConservativeRoots& conservativeRoots, Thread* thread)
454{
455 suspendThread(thread->platformThread);
456
457 PlatformThreadRegisters regs;
458 size_t regSize = getPlatformThreadRegisters(thread->platformThread, regs);
459
460 conservativeRoots.add(static_cast<void*>(&regs), static_cast<void*>(reinterpret_cast<char*>(&regs) + regSize));
461
462 void* stackPointer = otherThreadStackPointer(regs);
463 void* stackBase = thread->stackBase;
464 swapIfBackwards(stackPointer, stackBase);
465 conservativeRoots.add(stackPointer, stackBase);
466
467 resumeThread(thread->platformThread);
468
469 freePlatformThreadRegisters(regs);
470}
471
14957cd0
A
472void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, void* stackCurrent)
473{
474 gatherFromCurrentThread(conservativeRoots, stackCurrent);
475
14957cd0 476 if (m_threadSpecific) {
6fe7ccc8 477 PlatformThread currentPlatformThread = getCurrentPlatformThread();
14957cd0
A
478
479 MutexLocker lock(m_registeredThreadsMutex);
480
481#ifndef NDEBUG
482 // Forbid malloc during the gather phase. The gather phase suspends
483 // threads, so a malloc during gather would risk a deadlock with a
484 // thread that had been suspended while holding the malloc lock.
485 fastMallocForbid();
486#endif
487 // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
488 // and since this is a shared heap, they are real locks.
489 for (Thread* thread = m_registeredThreads; thread; thread = thread->next) {
6fe7ccc8 490 if (!equalThread(thread->platformThread, currentPlatformThread))
14957cd0
A
491 gatherFromOtherThread(conservativeRoots, thread);
492 }
493#ifndef NDEBUG
494 fastMallocAllow();
495#endif
496 }
14957cd0
A
497}
498
499} // namespace JSC