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