#include "JSContextRefPrivate.h"
#include "JavaScript.h"
#include "Options.h"
+
#include <chrono>
+#include <wtf/Atomics.h>
+#include <wtf/Condition.h>
#include <wtf/CurrentTime.h>
+#include <wtf/Lock.h>
#include <wtf/text/StringBuilder.h>
+#if HAVE(MACH_EXCEPTIONS)
+#include <dispatch/dispatch.h>
+#endif
+
using namespace std::chrono;
using JSC::Options;
return true;
}
+#if HAVE(MACH_EXCEPTIONS)
+bool dispatchTerminateCallbackCalled = false;
+static bool dispatchTermitateCallback(JSContextRef, void*)
+{
+ dispatchTerminateCallbackCalled = true;
+ return true;
+}
+#endif
+
struct TierOptions {
const char* tier;
unsigned timeLimitAdjustmentMillis;
}
}
+#if HAVE(MACH_EXCEPTIONS)
+ /* Test script timeout from dispatch queue: */
+ timeLimit = (100 + tierAdjustmentMillis) / 1000.0;
+ JSContextGroupSetExecutionTimeLimit(contextGroup, timeLimit, dispatchTermitateCallback, 0);
+ {
+ unsigned timeAfterWatchdogShouldHaveFired = 300 + tierAdjustmentMillis;
+
+ StringBuilder scriptBuilder;
+ scriptBuilder.appendLiteral("function foo() { var startTime = currentCPUTime(); while (true) { for (var i = 0; i < 1000; i++); if (currentCPUTime() - startTime > ");
+ scriptBuilder.appendNumber(timeAfterWatchdogShouldHaveFired / 1000.0);
+ scriptBuilder.appendLiteral(") break; } } foo();");
+
+ JSStringRef script = JSStringCreateWithUTF8CString(scriptBuilder.toString().utf8().data());
+ exception = nullptr;
+ dispatchTerminateCallbackCalled = false;
+
+ // We have to do this since blocks can only capture things as const.
+ JSGlobalContextRef& contextRef = context;
+ JSStringRef& scriptRef = script;
+ JSValueRef& exceptionRef = exception;
+
+ Lock syncLock;
+ Lock& syncLockRef = syncLock;
+ Condition synchronize;
+ Condition& synchronizeRef = synchronize;
+ bool didSynchronize = false;
+ bool& didSynchronizeRef = didSynchronize;
+
+ std::chrono::microseconds startTime;
+ std::chrono::microseconds endTime;
+
+ std::chrono::microseconds& startTimeRef = startTime;
+ std::chrono::microseconds& endTimeRef = endTime;
+
+ dispatch_group_t group = dispatch_group_create();
+ dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
+ startTimeRef = currentCPUTime();
+ JSEvaluateScript(contextRef, scriptRef, nullptr, nullptr, 1, &exceptionRef);
+ endTimeRef = currentCPUTime();
+ auto locker = WTF::holdLock(syncLockRef);
+ didSynchronizeRef = true;
+ synchronizeRef.notifyAll();
+ });
+
+ auto locker = holdLock(syncLock);
+ synchronize.wait(syncLock, [&] { return didSynchronize; });
+
+ if (((endTime - startTime) < milliseconds(timeAfterWatchdogShouldHaveFired)) && dispatchTerminateCallbackCalled)
+ printf("PASS: %s script on dispatch queue timed out as expected.\n", tierOptions.tier);
+ else {
+ if ((endTime - startTime) >= milliseconds(timeAfterWatchdogShouldHaveFired))
+ printf("FAIL: %s script on dispatch queue did not time out as expected.\n", tierOptions.tier);
+ if (!shouldTerminateCallbackWasCalled)
+ printf("FAIL: %s script on dispatch queue timeout callback was not called.\n", tierOptions.tier);
+ failed = true;
+ }
+ }
+#endif
+
JSGlobalContextRelease(context);
Options::setOptions(savedOptionsBuilder.toString().ascii().data());
+2017-05-31 Keith Miller <keith_miller@apple.com>
+
+ Reland r216808, underlying lldb bug has been fixed.
+ https://bugs.webkit.org/show_bug.cgi?id=172759
+
+
+ Unreviewed, relanding old patch. See: rdar://problem/31183352
+
+ * API/tests/ExecutionTimeLimitTest.cpp:
+ (dispatchTermitateCallback):
+ (testExecutionTimeLimit):
+ * runtime/JSLock.cpp:
+ (JSC::JSLock::didAcquireLock):
+ * runtime/Options.cpp:
+ (JSC::overrideDefaults):
+ (JSC::Options::initialize):
+ * runtime/Options.h:
+ * runtime/VMTraps.cpp:
+ (JSC::SignalContext::SignalContext):
+ (JSC::SignalContext::adjustPCToPointToTrappingInstruction):
+ (JSC::installSignalHandler):
+ (JSC::VMTraps::SignalSender::send):
+ * tools/SigillCrashAnalyzer.cpp:
+ (JSC::SignalContext::SignalContext):
+ (JSC::SignalContext::dump):
+ (JSC::installCrashHandler):
+ * wasm/WasmBBQPlan.cpp:
+ (JSC::Wasm::BBQPlan::compileFunctions):
+ * wasm/WasmFaultSignalHandler.cpp:
+ (JSC::Wasm::trapHandler):
+ (JSC::Wasm::enableFastMemory):
+ * wasm/WasmMachineThreads.cpp:
+ (JSC::Wasm::resetInstructionCacheOnAllThreads):
+
2017-05-31 Keith Miller <keith_miller@apple.com>
Fix leak in PromiseDeferredTimer
#include "SamplingProfiler.h"
#include "WasmMachineThreads.h"
#include <thread>
+#include <wtf/Threading.h>
+#include <wtf/threads/Signals.h>
namespace JSC {
Wasm::startTrackingCurrentThread();
#endif
+#if HAVE(MACH_EXCEPTIONS)
+ registerThreadForMachExceptionHandling(&Thread::current());
+#endif
+
m_vm->traps().notifyGrabAllLocks();
#if ENABLE(SAMPLING_PROFILER)
#include <wtf/StdLibExtras.h>
#include <wtf/StringExtras.h>
#include <wtf/text/StringBuilder.h>
+#include <wtf/threads/Signals.h>
#if PLATFORM(COCOA)
#include <crt_externs.h>
#if !ENABLE(WEBASSEMBLY_FAST_MEMORY)
Options::useWebAssemblyFastMemory() = false;
#endif
+
+#if !HAVE(MACH_EXCEPTIONS)
+ Options::useMachForExceptions() = false;
+#endif
}
static void recomputeDependentOptions()
dumpOptionsIfNeeded();
ensureOptionsAreCoherent();
+
+#if HAVE(MACH_EXCEPTIONS)
+ if (Options::useMachForExceptions())
+ handleSignalsWithMach();
+#endif
});
}
v(unsigned, watchdog, 0, Normal, "watchdog timeout (0 = Disabled, N = a timeout period of N milliseconds)") \
v(bool, usePollingTraps, false, Normal, "use polling (instead of signalling) VM traps") \
\
+ v(bool, useMachForExceptions, true, Normal, "Use mach exceptions rather than signals to handle faults and pass thread messages. (This does nothing on platforms without mach)") \
+ \
v(bool, useICStats, false, Normal, nullptr) \
\
v(unsigned, prototypeHitCountForLLIntCaching, 2, Normal, "Number of prototype property hits before caching a prototype in the LLInt. A count of 0 means never cache.") \
v(unsigned, webAssemblyBBQOptimizationLevel, 1, Normal, "B3 Optimization level for BBQ Web Assembly module compilations.") \
v(unsigned, webAssemblyOMGOptimizationLevel, Options::defaultB3OptLevel(), Normal, "B3 Optimization level for OMG Web Assembly module compilations.") \
\
+ v(bool, useBBQTierUpChecks, true, Normal, "Enables tier up checks for our BBQ code.") \
v(unsigned, webAssemblyOMGTierUpCount, 5000, Normal, "The countdown before we tier up a function to OMG.") \
v(unsigned, webAssemblyLoopDecrement, 15, Normal, "The amount the tier up countdown is decremented on each loop backedge.") \
v(unsigned, webAssemblyFunctionEntryDecrement, 1, Normal, "The amount the tier up countdown is decremented on each function entry.") \
#if ENABLE(SIGNAL_BASED_VM_TRAPS)
struct SignalContext {
- SignalContext(mcontext_t& mcontext)
- : mcontext(mcontext)
- , trapPC(MachineContext::instructionPointer(mcontext))
- , stackPointer(MachineContext::stackPointer(mcontext))
- , framePointer(MachineContext::framePointer(mcontext))
+ SignalContext(PlatformRegisters& registers)
+ : registers(registers)
+ , trapPC(MachineContext::instructionPointer(registers))
+ , stackPointer(MachineContext::stackPointer(registers))
+ , framePointer(MachineContext::framePointer(registers))
{
#if CPU(X86_64) || CPU(X86)
// On X86_64, SIGTRAP reports the address after the trapping PC. So, dec by 1.
void adjustPCToPointToTrappingInstruction()
{
#if CPU(X86_64) || CPU(X86)
- MachineContext::instructionPointer(mcontext) = trapPC;
+ MachineContext::instructionPointer(registers) = trapPC;
#endif
}
- mcontext_t& mcontext;
+ PlatformRegisters& registers;
void* trapPC;
void* stackPointer;
void* framePointer;
static void installSignalHandler()
{
- installSignalHandler(Signal::Trap, [] (int, siginfo_t*, void* uap) -> SignalAction {
- SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
+ installSignalHandler(Signal::Trap, [] (Signal, SigInfo&, PlatformRegisters& registers) -> SignalAction {
+ SignalContext context(registers);
if (!isJITPC(context.trapPC))
return SignalAction::NotHandled;
VM& vm = *m_vm;
auto optionalOwnerThread = vm.ownerThread();
if (optionalOwnerThread) {
- sendMessage(*optionalOwnerThread.value().get(), [] (siginfo_t*, ucontext_t* ucontext) -> void {
- SignalContext context(ucontext->uc_mcontext);
+ sendMessage(*optionalOwnerThread.value().get(), [] (PlatformRegisters& registers) -> void {
+ SignalContext context(registers);
auto activeVMAndStackBounds = findActiveVMAndStackBounds(context);
if (activeVMAndStackBounds) {
VM* vm = activeVMAndStackBounds.value().vm;
#endif // USE(OS_LOG)
struct SignalContext {
- SignalContext(mcontext_t& mcontext)
- : mcontext(mcontext)
- , machinePC(MachineContext::instructionPointer(mcontext))
- , stackPointer(MachineContext::stackPointer(mcontext))
- , framePointer(MachineContext::framePointer(mcontext))
+ SignalContext(PlatformRegisters& registers)
+ : registers(registers)
+ , machinePC(MachineContext::instructionPointer(registers))
+ , stackPointer(MachineContext::stackPointer(registers))
+ , framePointer(MachineContext::framePointer(registers))
{ }
void dump()
v(gs)
#define DUMP_REGISTER(__reg) \
- log("Register " #__reg ": %p", reinterpret_cast<void*>(mcontext->__ss.__##__reg));
+ log("Register " #__reg ": %p", reinterpret_cast<void*>(registers.__##__reg));
FOR_EACH_REGISTER(DUMP_REGISTER)
#undef FOR_EACH_REGISTER
int i;
for (i = 0; i < 28; i += 4) {
log("x%d: %016llx x%d: %016llx x%d: %016llx x%d: %016llx",
- i, mcontext->__ss.__x[i],
- i+1, mcontext->__ss.__x[i+1],
- i+2, mcontext->__ss.__x[i+2],
- i+3, mcontext->__ss.__x[i+3]);
+ i, registers.__x[i],
+ i+1, registers.__x[i+1],
+ i+2, registers.__x[i+2],
+ i+3, registers.__x[i+3]);
}
ASSERT(i < 29);
log("x%d: %016llx fp: %016llx lr: %016llx",
- i, mcontext->__ss.__x[i], mcontext->__ss.__fp, mcontext->__ss.__lr);
+ i, registers.__x[i], registers.__fp, registers.__lr);
log("sp: %016llx pc: %016llx cpsr: %08x",
- mcontext->__ss.__sp, mcontext->__ss.__pc, mcontext->__ss.__cpsr);
+ registers.__sp, registers.__pc, registers.__cpsr);
#endif
}
- mcontext_t& mcontext;
+ PlatformRegisters& registers;
void* machinePC;
void* stackPointer;
void* framePointer;
static void installCrashHandler()
{
#if CPU(X86_64) || CPU(ARM64)
- installSignalHandler(Signal::Ill, [] (int, siginfo_t*, void* uap) {
- SignalContext context(static_cast<ucontext_t*>(uap)->uc_mcontext);
+ installSignalHandler(Signal::Ill, [] (Signal, SigInfo&, PlatformRegisters& registers) {
+ SignalContext context(registers);
if (!isJITPC(context.machinePC))
return SignalAction::NotHandled;
ASSERT(validateFunction(functionStart, functionLength, signature, m_moduleInformation.get()));
m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>();
- TierUpCount* tierUp = &m_tierUpCounts[functionIndex];
+ TierUpCount* tierUp = Options::useBBQTierUpChecks() ? &m_tierUpCounts[functionIndex] : nullptr;
auto parseAndCompileResult = parseAndCompile(m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], m_moduleInformation.get(), m_mode, CompilationMode::BBQMode, functionIndex, tierUp);
if (UNLIKELY(!parseAndCompileResult)) {
static bool fastHandlerInstalled { false };
-static SignalAction trapHandler(int, siginfo_t* sigInfo, void* ucontext)
+static SignalAction trapHandler(Signal, SigInfo& sigInfo, PlatformRegisters& context)
{
- mcontext_t& context = static_cast<ucontext_t*>(ucontext)->uc_mcontext;
void* faultingInstruction = MachineContext::instructionPointer(context);
dataLogLnIf(verbose, "starting handler for fault at: ", RawPointer(faultingInstruction));
if (isJITPC(faultingInstruction)) {
bool faultedInActiveFastMemory = false;
{
- void* faultingAddress = sigInfo->si_addr;
+ void* faultingAddress = sigInfo.faultingAddress;
dataLogLnIf(verbose, "checking faulting address: ", RawPointer(faultingAddress), " is in an active fast memory");
faultedInActiveFastMemory = Wasm::Memory::addressIsInActiveFastMemory(faultingAddress);
}
return;
#if ENABLE(WEBASSEMBLY_FAST_MEMORY)
- installSignalHandler(Signal::Bus, [] (int signal, siginfo_t* sigInfo, void* ucontext) {
+ installSignalHandler(Signal::Bus, [] (Signal signal, SigInfo& sigInfo, PlatformRegisters& ucontext) {
return trapHandler(signal, sigInfo, ucontext);
});
- installSignalHandler(Signal::SegV, [] (int signal, siginfo_t* sigInfo, void* ucontext) {
+ installSignalHandler(Signal::SegV, [] (Signal signal, SigInfo& sigInfo, PlatformRegisters& ucontext) {
return trapHandler(signal, sigInfo, ucontext);
});
#include "MachineStackMarker.h"
#include <wtf/NeverDestroyed.h>
#include <wtf/ThreadMessage.h>
+#include <wtf/threads/Signals.h>
namespace JSC { namespace Wasm {
const DoublyLinkedList<MachineThreads::MachineThread>& threads = wasmThreads().threadsListHead(locker);
for (const auto* thread = threads.head(); thread; thread = thread->next()) {
- sendMessage(thread->m_thread.get(), [] (siginfo_t*, ucontext_t*) {
+ sendMessage(thread->m_thread.get(), [] (const PlatformRegisters&) {
// It's likely that the signal handler will already reset the instruction cache but we might as well be sure.
WTF::crossModifyingCodeFence();
});
+2017-05-31 Keith Miller <keith_miller@apple.com>
+
+ Reland r216808, underlying lldb bug has been fixed.
+ https://bugs.webkit.org/show_bug.cgi?id=172759
+
+ Unreviewed, relanding old patch. See: rdar://problem/31183352
+
+ * Configurations/WTF.xcconfig:
+ * WTF.xcodeproj/project.pbxproj:
+ * wtf/Platform.h:
+ * wtf/PlatformRegisters.h:
+ (WTF::registersFromUContext):
+ * wtf/StackBounds.h:
+ (WTF::StackBounds::StackBounds):
+ * wtf/ThreadHolder.cpp:
+ (WTF::ThreadHolder::~ThreadHolder):
+ * wtf/ThreadMessage.cpp:
+ (WTF::sendMessageUsingSignal):
+ (WTF::sendMessageUsingMach):
+ (WTF::deliverMessagesUsingMach):
+ (WTF::sendMessageScoped):
+ * wtf/ThreadMessage.h:
+ (WTF::sendMessage):
+ * wtf/Threading.h:
+ (WTF::Thread::machThread):
+ * wtf/mac/MachExceptions.defs: Copied from Source/WTF/wtf/ThreadMessage.h.
+ * wtf/threads/Signals.cpp:
+ (WTF::startMachExceptionHandlerThread):
+ (WTF::fromMachException):
+ (WTF::toMachMask):
+ (WTF::handleSignalsWithMach):
+ (WTF::setExceptionPorts):
+ (WTF::activeThreads):
+ (WTF::registerThreadForMachExceptionHandling):
+ (WTF::unregisterThreadForMachExceptionHandling):
+ (WTF::installSignalHandler):
+ (WTF::jscSignalHandler):
+ * wtf/threads/Signals.h:
+
2017-05-31 Matt Lewis <jlewis3@apple.com>
Unreviewed, rolling out r217603.
GCC_SYMBOLS_PRIVATE_EXTERN = YES;
STRIP_INSTALLED_PRODUCT = NO;
+
+EXCLUDED_SOURCE_FILE_NAMES_ = MachExceptions.defs
+EXCLUDED_SOURCE_FILE_NAMES[sdk=iphoneos*] = $(EXCLUDED_SOURCE_FILE_NAMES_$(USE_INTERNAL_SDK))
52183012C99E476A84EEBEA8 /* SymbolImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F72BBDB107FA424886178B9E /* SymbolImpl.cpp */; };
5311BD531EA71CAD00525281 /* Signals.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5311BD511EA71CAD00525281 /* Signals.cpp */; };
5311BD5C1EA822F900525281 /* ThreadMessage.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5311BD5B1EA822F900525281 /* ThreadMessage.cpp */; };
+ 53534F2A1EC0E10E00141B2F /* MachExceptions.defs in Sources */ = {isa = PBXBuildFile; fileRef = 53534F291EC0E10E00141B2F /* MachExceptions.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; };
70A993FE1AD7151300FA615B /* SymbolRegistry.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70A993FC1AD7151300FA615B /* SymbolRegistry.cpp */; };
70ECA60D1B02426800449739 /* AtomicStringImpl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 70ECA60A1B02426800449739 /* AtomicStringImpl.cpp */; };
7AFEC6B11EB22B5900DADE36 /* UUID.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AFEC6B01EB22B5900DADE36 /* UUID.cpp */; };
5311BD571EA7E1A100525281 /* Signals.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Signals.h; sourceTree = "<group>"; };
5311BD591EA81A9600525281 /* ThreadMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThreadMessage.h; sourceTree = "<group>"; };
5311BD5B1EA822F900525281 /* ThreadMessage.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThreadMessage.cpp; sourceTree = "<group>"; };
+ 53534F291EC0E10E00141B2F /* MachExceptions.defs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.mig; path = MachExceptions.defs; sourceTree = "<group>"; };
539EB0621D55284200C82EF7 /* LEBDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LEBDecoder.h; sourceTree = "<group>"; };
53EC253C1E95AD30000831B9 /* PriorityQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PriorityQueue.h; sourceTree = "<group>"; };
553071C91C40427200384898 /* TinyLRUCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TinyLRUCache.h; sourceTree = "<group>"; };
children = (
1A428B8B1C8F89DD0051E9EB /* AppKitCompatibilityDeclarations.h */,
1ACADD821884480100D8B71D /* DeprecatedSymbolsUsedBySafari.mm */,
+ 53534F291EC0E10E00141B2F /* MachExceptions.defs */,
A8A472C5151A825A004123FF /* MainThreadMac.mm */,
);
path = mac;
143F611F1565F0F900DB514A /* RAMSize.cpp in Sources */,
A3B725EC987446AD93F1A440 /* RandomDevice.cpp in Sources */,
A8A47414151A825B004123FF /* RandomNumber.cpp in Sources */,
+ 53534F2A1EC0E10E00141B2F /* MachExceptions.defs in Sources */,
A8A4741A151A825B004123FF /* RefCountedLeakCounter.cpp in Sources */,
2CDED0F318115C85004DBA70 /* RunLoop.cpp in Sources */,
2CDED0EF18115C38004DBA70 /* RunLoopCF.cpp in Sources */,
#define HAVE_READLINE 1
#define HAVE_SYS_TIMEB_H 1
+#if __has_include(<mach/mach_exc.defs>) && !(PLATFORM(WATCHOS) || PLATFORM(APPLETV))
+#define HAVE_MACH_EXCEPTIONS 1
+#endif
+
#if !PLATFORM(GTK)
#define USE_ACCELERATE 1
#endif
#pragma once
#include <wtf/Platform.h>
+#include <wtf/StdLibExtras.h>
#if OS(DARWIN)
#include <mach/thread_act.h>
+#include <signal.h>
#elif OS(WINDOWS)
#include <windows.h>
#else
#error Unknown Architecture
#endif
+inline PlatformRegisters& registersFromUContext(ucontext_t* ucontext)
+{
+ return ucontext->uc_mcontext->__ss;
+}
+
#elif OS(WINDOWS)
using PlatformRegisters = CONTEXT;
mcontext_t machineContext;
};
+inline PlatformRegisters& registersFromUContext(ucontext_t* ucontext)
+{
+ return *bitwise_cast<PlatformRegisters*>(&ucontext->uc_mcontext);
+}
+
#else
struct PlatformRegisters {
: m_origin(origin)
, m_bound(end)
{
- checkConsistency();
}
void* origin() const
#include "Threading.h"
+#include <wtf/threads/Signals.h>
+
namespace WTF {
ThreadSpecificKey ThreadHolder::m_key = InvalidThreadSpecificKey;
ThreadHolder::~ThreadHolder()
{
+#if HAVE(MACH_EXCEPTIONS)
+ unregisterThreadForMachExceptionHandling(&m_thread.get());
+#endif
m_thread->didExit();
}
#include <wtf/Locker.h>
#include <wtf/threads/Signals.h>
+#if HAVE(MACH_EXCEPTIONS)
+#include <mach/thread_act.h>
+#endif
namespace WTF {
}
SUPPRESS_ASAN
-MessageStatus sendMessageScoped(Thread& thread, const ThreadMessage& message)
+static MessageStatus sendMessageUsingSignal(Thread& thread, const ThreadMessage& message)
{
constexpr Signal signal = Signal::Usr;
static std::once_flag once;
std::call_once(once, [] {
- installSignalHandler(signal, [] (int, siginfo_t* info, void* uap) {
+ installSignalHandler(signal, [] (Signal, SigInfo&, PlatformRegisters& registers) {
Thread* thread = Thread::currentMayBeNull();
if (!thread) {
// Node should be deleted in the sender thread. Deleting Nodes in signal handler causes dead lock.
thread->threadMessages().consumeAllWithNode([&] (ThreadMessageData* data, Node* node) {
- data->message(info, static_cast<ucontext_t*>(uap));
+ data->message(registers);
// By setting ran variable, (1) the sender acknowledges the completion and
// (2) gets the Node to be deleted.
data->ran.store(node);
RELEASE_ASSERT_NOT_REACHED();
}
+#if HAVE(MACH_EXCEPTIONS)
+static MessageStatus sendMessageUsingMach(Thread& thread, const ThreadMessage& message)
+{
+ static StaticLock messageLock;
+ auto lockholder = holdLock(messageLock);
+
+ auto result = thread.suspend();
+ if (!result)
+ return MessageStatus::ThreadExited;
+
+#if CPU(X86)
+ unsigned userCount = sizeof(PlatformRegisters) / sizeof(int);
+ thread_state_flavor_t flavor = i386_THREAD_STATE;
+#elif CPU(X86_64)
+ unsigned userCount = x86_THREAD_STATE64_COUNT;
+ thread_state_flavor_t flavor = x86_THREAD_STATE64;
+#elif CPU(ARM)
+ unsigned userCount = ARM_THREAD_STATE_COUNT;
+ thread_state_flavor_t flavor = ARM_THREAD_STATE;
+#elif CPU(ARM64)
+ unsigned userCount = ARM_THREAD_STATE64_COUNT;
+ thread_state_flavor_t flavor = ARM_THREAD_STATE64;
+#else
+#error Unknown Architecture
+#endif
+
+ PlatformRegisters registers;
+ thread_state_t state = reinterpret_cast<thread_state_t>(®isters);
+ thread_get_state(thread.machThread(), flavor, state, &userCount);
+
+ message(registers);
+
+ thread_set_state(thread.machThread(), flavor, state, userCount);
+
+ thread.resume();
+ return MessageStatus::MessageRan;
+}
+
+static bool useMach = false;
+void deliverMessagesUsingMach()
+{
+ useMach = true;
+}
+
+#endif // HAVE(MACH_EXCEPTIONS)
+
+MessageStatus sendMessageScoped(Thread& thread, const ThreadMessage& message)
+{
+#if HAVE(MACH_EXCEPTIONS)
+ if (useMach)
+ return sendMessageUsingMach(thread, message);
+#endif
+ return sendMessageUsingSignal(thread, message);
+}
+
+
} // namespace WTF
#endif // USE(PTHREADS)
void initializeThreadMessages();
class ThreadMessageData;
-using ThreadMessage = ScopedLambda<void(siginfo_t*, ucontext_t*)>;
+using ThreadMessage = ScopedLambda<void(PlatformRegisters&)>;
enum class MessageStatus {
MessageRan,
template<typename Functor>
MessageStatus sendMessage(Thread& targetThread, const Functor& func)
{
- auto lambda = scopedLambdaRef<void(siginfo_t*, ucontext_t*)>(func);
+ auto lambda = scopedLambdaRef<void(PlatformRegisters&)>(func);
return sendMessageScoped(targetThread, lambda);
}
+#if HAVE(MACH_EXCEPTIONS)
+void deliverMessagesUsingMach();
+#endif
+
} // namespace WTF
using WTF::sendMessage;
#if USE(PTHREADS)
LocklessBag<ThreadMessageData*>& threadMessages() { return m_threadMessages; }
+
+#if OS(DARWIN)
+ mach_port_t machThread() { return m_platformThread; }
+#endif
#endif
protected:
--- /dev/null
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <mach/mach_exc.defs>
#if USE(PTHREADS)
+#if HAVE(MACH_EXCEPTIONS)
+extern "C" {
+#include "MachExceptionsServer.h"
+};
+#endif
+
#include <cstdio>
#include <mutex>
#include <signal.h>
+
+#if HAVE(MACH_EXCEPTIONS)
+#include <mach/mach.h>
+#include <mach/thread_act.h>
+#endif
+
#include <wtf/Atomics.h>
#include <wtf/DataLog.h>
+#include <wtf/HashSet.h>
#include <wtf/LocklessBag.h>
#include <wtf/NeverDestroyed.h>
+#include <wtf/ThreadMessage.h>
+#include <wtf/Threading.h>
+
namespace WTF {
+
static LazyNeverDestroyed<LocklessBag<SignalHandler>> handlers[static_cast<size_t>(Signal::NumberOfSignals)] = { };
-static struct sigaction oldActions[static_cast<size_t>(Signal::NumberOfSignals)];
static std::once_flag initializeOnceFlags[static_cast<size_t>(Signal::NumberOfSignals)];
+static struct sigaction oldActions[static_cast<size_t>(Signal::NumberOfSignals)];
+
+#if HAVE(MACH_EXCEPTIONS)
+// You can read more about mach exceptions here:
+// http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/exception.ps
+// and the Mach interface Generator (MiG) here:
+// http://www.cs.cmu.edu/afs/cs/project/mach/public/doc/unpublished/mig.ps
+
+static mach_port_t exceptionPort;
+static constexpr size_t maxMessageSize = 1 * KB;
+
+static void startMachExceptionHandlerThread()
+{
+ static std::once_flag once;
+ std::call_once(once, [] {
+ if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exceptionPort) != KERN_SUCCESS)
+ CRASH();
+
+ if (mach_port_insert_right(mach_task_self(), exceptionPort, exceptionPort, MACH_MSG_TYPE_MAKE_SEND) != KERN_SUCCESS)
+ CRASH();
+
+ // FIXME: This should use a dispatch queue.
+ // See: https://bugs.webkit.org/show_bug.cgi?id=172003
+ (void)Thread::create(
+ "WTF Mach Exception Thread", [] () {
+ union Message {
+ mach_msg_header_t header;
+ char data[maxMessageSize];
+ };
+ Message messageHeaderIn;
+ Message messageHeaderOut;
+
+ while (true) {
+ kern_return_t messageResult = mach_msg(&messageHeaderIn.header, MACH_RCV_MSG, 0, maxMessageSize, exceptionPort, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+ if (messageResult == KERN_SUCCESS) {
+ if (!mach_exc_server(&messageHeaderIn.header, &messageHeaderOut.header))
+ CRASH();
+
+ messageResult = mach_msg(&messageHeaderOut.header, MACH_SEND_MSG, messageHeaderOut.header.msgh_size, 0, messageHeaderOut.header.msgh_local_port, 0, MACH_PORT_NULL);
+ RELEASE_ASSERT(messageResult == KERN_SUCCESS);
+ } else {
+ dataLogLn("Failed to receive mach message due to ", mach_error_string(messageResult));
+ RELEASE_ASSERT_NOT_REACHED();
+ }
+ }
+ }).leakRef();
+ });
+}
+
+static Signal fromMachException(exception_type_t type)
+{
+ switch (type) {
+ case EXC_BAD_ACCESS: return Signal::SegV;
+ case EXC_BAD_INSTRUCTION: return Signal::Ill;
+ case EXC_BREAKPOINT: return Signal::Trap;
+ default: break;
+ }
+ return Signal::Unknown;
+}
+
+static exception_mask_t toMachMask(Signal signal)
+{
+ switch (signal) {
+ case Signal::SegV: return EXC_MASK_BAD_ACCESS;
+ case Signal::Ill: return EXC_MASK_BAD_INSTRUCTION;
+ case Signal::Trap: return EXC_MASK_BREAKPOINT;
+ default: break;
+ }
+ RELEASE_ASSERT_NOT_REACHED();
+}
+
+extern "C" {
+
+// We need to implement stubs for catch_mach_exception_raise and catch_mach_exception_raise_state_identity.
+// The MiG generated file will fail to link otherwise, even though we don't use the functions. Only the
+// catch_mach_exception_raise_state function should be called because we pass EXCEPTION_STATE to
+// thread_set_exception_ports.
+kern_return_t catch_mach_exception_raise(mach_port_t, mach_port_t, mach_port_t, exception_type_t, mach_exception_data_t, mach_msg_type_number_t)
+{
+ dataLogLn("We should not have called catch_exception_raise(), please file a bug at bugs.webkit.org");
+ return KERN_FAILURE;
+}
+
+kern_return_t catch_mach_exception_raise_state_identity(mach_port_t, mach_port_t, mach_port_t, exception_type_t, mach_exception_data_t, mach_msg_type_number_t, int*, thread_state_t, mach_msg_type_number_t, thread_state_t, mach_msg_type_number_t*)
+{
+ dataLogLn("We should not have called catch_mach_exception_raise_state_identity, please file a bug at bugs.webkit.org");
+ return KERN_FAILURE;
+}
+
+kern_return_t catch_mach_exception_raise_state(
+ mach_port_t port,
+ exception_type_t exceptionType,
+ const mach_exception_data_t exceptionData,
+ mach_msg_type_number_t dataCount,
+ int* stateFlavor,
+ const thread_state_t inState,
+ mach_msg_type_number_t inStateCount,
+ thread_state_t outState,
+ mach_msg_type_number_t* outStateCount)
+{
+ RELEASE_ASSERT(port == exceptionPort);
+ Signal signal = fromMachException(exceptionType);
+ RELEASE_ASSERT(signal != Signal::Unknown);
+
+ memcpy(outState, inState, inStateCount * sizeof(inState[0]));
+ *outStateCount = inStateCount;
+
+#if CPU(X86_64)
+ RELEASE_ASSERT(*stateFlavor == x86_THREAD_STATE);
+ PlatformRegisters& registers = reinterpret_cast<x86_thread_state_t*>(outState)->uts.ts64;
+#elif CPU(X86)
+ RELEASE_ASSERT(*stateFlavor == x86_THREAD_STATE);
+ PlatformRegisters& registers = reinterpret_cast<x86_thread_state_t*>(outState)->uts.ts32;
+#elif CPU(ARM64)
+ RELEASE_ASSERT(*stateFlavor == ARM_THREAD_STATE);
+ PlatformRegisters& registers = reinterpret_cast<arm_unified_thread_state*>(outState)->ts_64;
+#elif CPU(ARM)
+ RELEASE_ASSERT(*stateFlavor == ARM_THREAD_STATE);
+ PlatformRegisters& registers = reinterpret_cast<arm_unified_thread_state*>(outState)->ts_32;
+#endif
+
+ SigInfo info;
+ if (signal == Signal::SegV) {
+ ASSERT_UNUSED(dataCount, dataCount == 2);
+ info.faultingAddress = reinterpret_cast<void*>(exceptionData[1]);
+ }
+
+ bool didHandle = false;
+ handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
+ SignalAction handlerResult = handler(signal, info, registers);
+ didHandle |= handlerResult == SignalAction::Handled;
+ });
+
+ if (didHandle)
+ return KERN_SUCCESS;
+ return KERN_FAILURE;
+}
+
+};
+
+static bool useMach { false };
+void handleSignalsWithMach()
+{
+ deliverMessagesUsingMach();
+ useMach = true;
+}
+
+
+static StaticLock threadLock;
+exception_mask_t activeExceptions { 0 };
-static void jscSignalHandler(int sig, siginfo_t* info, void* mcontext)
+inline void setExceptionPorts(const AbstractLocker&, Thread* thread)
+{
+ kern_return_t result = thread_set_exception_ports(thread->machThread(), activeExceptions, exceptionPort, EXCEPTION_STATE | MACH_EXCEPTION_CODES, MACHINE_THREAD_STATE);
+ if (result != KERN_SUCCESS) {
+ dataLogLn("thread set port failed due to ", mach_error_string(result));
+ CRASH();
+ }
+}
+
+inline HashSet<Thread*>& activeThreads(const AbstractLocker&)
+{
+ static NeverDestroyed<HashSet<Thread*>> activeThreads;
+ return activeThreads;
+}
+
+void registerThreadForMachExceptionHandling(Thread* thread)
+{
+ auto locker = holdLock(threadLock);
+ auto result = activeThreads(locker).add(thread);
+
+ if (result.isNewEntry)
+ setExceptionPorts(locker, thread);
+}
+
+void unregisterThreadForMachExceptionHandling(Thread* thread)
+{
+ auto locker = holdLock(threadLock);
+ activeThreads(locker).remove(thread);
+}
+
+#else
+static constexpr bool useMach = false;
+#endif // HAVE(MACH_EXCEPTIONS)
+
+static void jscSignalHandler(int, siginfo_t*, void*);
+
+void installSignalHandler(Signal signal, SignalHandler&& handler)
+{
+ ASSERT(signal < Signal::Unknown);
+#if HAVE(MACH_EXCEPTIONS)
+ // Since mach only has EXC_BAD_ACCESS, which covers both SegV and Bus, we arbitarily choose to make
+ // mach EXC_BAD_ACCESSes map to SegV.
+ // FIXME: We should just use a single Signal::BadAccess value instead of SegV/Bus.
+ // See: https://bugs.webkit.org/show_bug.cgi?id=172063
+ if (signal == Signal::Bus && useMach)
+ return;
+ ASSERT(!useMach || signal != Signal::Usr);
+
+ if (useMach)
+ startMachExceptionHandlerThread();
+#endif
+
+ std::call_once(initializeOnceFlags[static_cast<size_t>(signal)], [&] {
+ handlers[static_cast<size_t>(signal)].construct();
+
+ if (!useMach) {
+ struct sigaction action;
+ action.sa_sigaction = jscSignalHandler;
+ auto result = sigfillset(&action.sa_mask);
+ RELEASE_ASSERT(!result);
+ action.sa_flags = SA_SIGINFO;
+ result = sigaction(toSystemSignal(signal), &action, &oldActions[static_cast<size_t>(signal)]);
+ RELEASE_ASSERT(!result);
+ }
+
+ });
+
+ handlers[static_cast<size_t>(signal)]->add(WTFMove(handler));
+
+#if HAVE(MACH_EXCEPTIONS)
+ auto locker = holdLock(threadLock);
+ if (useMach) {
+ activeExceptions |= toMachMask(signal);
+
+ for (Thread* thread : activeThreads(locker))
+ setExceptionPorts(locker, thread);
+ }
+#endif
+}
+
+void jscSignalHandler(int sig, siginfo_t* info, void* ucontext)
{
Signal signal = fromSystemSignal(sig);
return;
}
+ SigInfo sigInfo;
+ if (signal == Signal::SegV || signal == Signal::Bus)
+ sigInfo.faultingAddress = info->si_addr;
+
+ PlatformRegisters& registers = registersFromUContext(reinterpret_cast<ucontext_t*>(ucontext));
+
bool didHandle = false;
bool restoreDefaultHandler = false;
handlers[static_cast<size_t>(signal)]->iterate([&] (const SignalHandler& handler) {
- switch (handler(sig, info, mcontext)) {
+ switch (handler(signal, sigInfo, registers)) {
case SignalAction::Handled:
didHandle = true;
break;
struct sigaction& oldAction = oldActions[static_cast<size_t>(signal)];
if (signal == Signal::Usr) {
if (oldAction.sa_sigaction)
- oldAction.sa_sigaction(sig, info, mcontext);
+ oldAction.sa_sigaction(sig, info, ucontext);
return;
}
if (!didHandle) {
if (oldAction.sa_sigaction) {
- oldAction.sa_sigaction(sig, info, mcontext);
+ oldAction.sa_sigaction(sig, info, ucontext);
return;
}
}
}
-void installSignalHandler(Signal signal, SignalHandler&& handler)
-{
- ASSERT(signal < Signal::Unknown);
- std::call_once(initializeOnceFlags[static_cast<size_t>(signal)], [&] {
- handlers[static_cast<size_t>(signal)].construct();
-
- struct sigaction action;
- action.sa_sigaction = jscSignalHandler;
- auto result = sigfillset(&action.sa_mask);
- RELEASE_ASSERT(!result);
- action.sa_flags = SA_SIGINFO;
- result = sigaction(toSystemSignal(signal), &action, &oldActions[static_cast<size_t>(signal)]);
- RELEASE_ASSERT(!result);
- });
-
- handlers[static_cast<size_t>(signal)]->add(WTFMove(handler));
-}
-
} // namespace WTF
#endif // USE(PTHREADS)
#include <signal.h>
#include <wtf/Function.h>
+#include <wtf/PlatformRegisters.h>
namespace WTF {
ForceDefault
};
-using SignalHandler = Function<SignalAction(int, siginfo_t*, void*)>;
+struct SigInfo {
+ void* faultingAddress { 0 };
+};
+
+using SignalHandler = Function<SignalAction(Signal, SigInfo&, PlatformRegisters&)>;
// Call this method whenever you want to install a signal handler. It's ok to call this function lazily.
// Note: Your signal handler will be called every time the handler for the desired signal is called.
// This function is currently a one way street i.e. once installed, a signal handler cannot be uninstalled.
WTF_EXPORT_PRIVATE void installSignalHandler(Signal, SignalHandler&&);
+
+#if HAVE(MACH_EXCEPTIONS)
+class Thread;
+void registerThreadForMachExceptionHandling(Thread*);
+void unregisterThreadForMachExceptionHandling(Thread*);
+
+void handleSignalsWithMach();
+#endif // HAVE(MACH_EXCEPTIONS)
+
} // namespace WTF
+#if HAVE(MACH_EXCEPTIONS)
+using WTF::registerThreadForMachExceptionHandling;
+using WTF::unregisterThreadForMachExceptionHandling;
+using WTF::handleSignalsWithMach;
+#endif // HAVE(MACH_EXCEPTIONS)
+
using WTF::Signal;
+using WTF::SigInfo;
using WTF::toSystemSignal;
using WTF::fromSystemSignal;
using WTF::SignalAction;
+2017-05-31 Keith Miller <keith_miller@apple.com>
+
+ Reland r216808, underlying lldb bug has been fixed.
+ https://bugs.webkit.org/show_bug.cgi?id=172759
+
+ Unreviewed, relanding old patch. See: rdar://problem/31183352
+
+ * TestWebKitAPI/Tests/WTF/ThreadMessages.cpp:
+ (runThreadMessageTest):
+ (TEST):
+
2017-05-31 Matt Lewis <jlewis3@apple.com>
Unreviewed, rolling out r217603.
for (unsigned senderID = 0; senderID < numSenders; ++senderID) {
senderThreads[senderID] = Thread::create("ThreadMessage sender", [senderID, numMessages, receiverThread, &messagesRun, &handlersRun] () {
for (unsigned i = 0; i < numMessages; ++i) {
- auto result = sendMessage(*receiverThread.get(), [senderID, &handlersRun] (siginfo_t*, ucontext_t*) {
+ auto result = sendMessage(*receiverThread.get(), [senderID, &handlersRun] (PlatformRegisters&) {
handlersRun[senderID]++;
});
EXPECT_TRUE(result == WTF::MessageStatus::MessageRan);
TEST(ThreadMessage, SignalsWorkOnExit)
{
static bool handlerRan = false;
- installSignalHandler(Signal::Usr, [] (int, siginfo_t*, void*) -> SignalAction {
+ installSignalHandler(Signal::Usr, [] (Signal, SigInfo&, PlatformRegisters&) -> SignalAction {
dataLogLn("here");
handlerRan = true;
return SignalAction::Handled;