]> git.saurik.com Git - apple/javascriptcore.git/blob - heap/MachineStackMarker.cpp
JavaScriptCore-1097.3.3.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 "JSGlobalData.h"
29 #include <setjmp.h>
30 #include <stdlib.h>
31 #include <wtf/StdLibExtras.h>
32
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
46 #elif OS(UNIX)
47
48 #include <stdlib.h>
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 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>
71 #endif
72
73 #endif
74
75 using namespace WTF;
76
77 namespace JSC {
78
79 static 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
86 UNUSED_PARAM(begin);
87 UNUSED_PARAM(end);
88 #endif
89 }
90
91 #if OS(DARWIN)
92 typedef mach_port_t PlatformThread;
93 #elif OS(WINDOWS)
94 typedef HANDLE PlatformThread;
95 #elif USE(PTHREADS)
96 typedef pthread_t PlatformThread;
97 static const int SigThreadSuspendResume = SIGUSR2;
98
99 #if defined(SA_RESTART)
100 static void pthreadSignalHandlerSuspendResume(int signo)
101 {
102 sigset_t signalSet;
103 sigemptyset(&signalSet);
104 sigaddset(&signalSet, SigThreadSuspendResume);
105 sigsuspend(&signalSet);
106 }
107 #endif
108 #endif
109
110 class MachineThreads::Thread {
111 public:
112 Thread(const PlatformThread& platThread, void* base)
113 : platformThread(platThread)
114 , stackBase(base)
115 {
116 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && defined(SA_RESTART)
117 // if we have SA_RESTART, enable SIGUSR2 debugging mechanism
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;
132 PlatformThread platformThread;
133 void* stackBase;
134 };
135
136 MachineThreads::MachineThreads(Heap* heap)
137 : m_heap(heap)
138 , m_registeredThreads(0)
139 , m_threadSpecific(0)
140 {
141 }
142
143 MachineThreads::~MachineThreads()
144 {
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 }
156 }
157
158 static inline PlatformThread getCurrentPlatformThread()
159 {
160 #if OS(DARWIN)
161 return pthread_mach_thread_np(pthread_self());
162 #elif OS(WINDOWS)
163 return GetCurrentThread();
164 #elif USE(PTHREADS)
165 return pthread_self();
166 #endif
167 }
168
169 static 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
180 void 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
190 void 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);
198 Thread* thread = new Thread(getCurrentPlatformThread(), wtfThreadData().stack().origin());
199
200 MutexLocker lock(m_registeredThreadsMutex);
201
202 thread->next = m_registeredThreads;
203 m_registeredThreads = thread;
204 }
205
206 void MachineThreads::removeThread(void* p)
207 {
208 if (p)
209 static_cast<MachineThreads*>(p)->removeCurrentThread();
210 }
211
212 void MachineThreads::removeCurrentThread()
213 {
214 PlatformThread currentPlatformThread = getCurrentPlatformThread();
215
216 MutexLocker lock(m_registeredThreadsMutex);
217
218 if (equalThread(currentPlatformThread, m_registeredThreads->platformThread)) {
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) {
226 if (equalThread(t->platformThread, currentPlatformThread)) {
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
237 #if COMPILER(GCC)
238 #define REGISTER_BUFFER_ALIGNMENT __attribute__ ((aligned (sizeof(void*))))
239 #else
240 #define REGISTER_BUFFER_ALIGNMENT
241 #endif
242
243 void 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;
262 void* stackEnd = wtfThreadData().stack().origin();
263 swapIfBackwards(stackBegin, stackEnd);
264 conservativeRoots.add(stackBegin, stackEnd);
265 }
266
267 static 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
280 static 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
293 typedef unsigned long usword_t; // word size, assumed to be either 32 or 64 bit
294
295 #if OS(DARWIN)
296
297 #if CPU(X86)
298 typedef i386_thread_state_t PlatformThreadRegisters;
299 #elif CPU(X86_64)
300 typedef x86_thread_state64_t PlatformThreadRegisters;
301 #elif CPU(PPC)
302 typedef ppc_thread_state_t PlatformThreadRegisters;
303 #elif CPU(PPC64)
304 typedef ppc_thread_state64_t PlatformThreadRegisters;
305 #elif CPU(ARM)
306 typedef arm_thread_state_t PlatformThreadRegisters;
307 #else
308 #error Unknown Architecture
309 #endif
310
311 #elif OS(WINDOWS)
312 typedef CONTEXT PlatformThreadRegisters;
313 #elif OS(QNX)
314 typedef struct _debug_thread_info PlatformThreadRegisters;
315 #elif USE(PTHREADS)
316 typedef pthread_attr_t PlatformThreadRegisters;
317 #else
318 #error Need a thread register struct for this platform
319 #endif
320
321 static 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)
354 regs.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
355 GetThreadContext(platformThread, &regs);
356 return sizeof(CONTEXT);
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);
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
382 static 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)
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)
422 return reinterpret_cast<void*>((uintptr_t) regs.Esp);
423 #elif CPU(X86_64)
424 return reinterpret_cast<void*>((uintptr_t) regs.Rsp);
425 #else
426 #error Unknown Architecture
427 #endif
428
429 #elif OS(QNX)
430 return reinterpret_cast<void*>((uintptr_t) regs.sp);
431
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
444 static void freePlatformThreadRegisters(PlatformThreadRegisters& regs)
445 {
446 #if USE(PTHREADS) && !OS(WINDOWS) && !OS(DARWIN) && !OS(QNX)
447 pthread_attr_destroy(&regs);
448 #else
449 UNUSED_PARAM(regs);
450 #endif
451 }
452
453 void 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
472 void MachineThreads::gatherConservativeRoots(ConservativeRoots& conservativeRoots, void* stackCurrent)
473 {
474 gatherFromCurrentThread(conservativeRoots, stackCurrent);
475
476 if (m_threadSpecific) {
477 PlatformThread currentPlatformThread = getCurrentPlatformThread();
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) {
490 if (!equalThread(thread->platformThread, currentPlatformThread))
491 gatherFromOtherThread(conservativeRoots, thread);
492 }
493 #ifndef NDEBUG
494 fastMallocAllow();
495 #endif
496 }
497 }
498
499 } // namespace JSC