Changeset 253581 in webkit


Ignore:
Timestamp:
Dec 16, 2019, 2:34:50 PM (5 years ago)
Author:
mark.lam@apple.com
Message:

Changed jsc shell timeout mechanism to leverage the VMTraps and use CPUTime.
https://bugs.webkit.org/show_bug.cgi?id=205279
<rdar://problem/57971874>

Reviewed by Saam Barati.

This fixes all the timeouts that occur due to CPU time starvation when
running JSC tests on a debug build.

What this means is that the timeout mechanism may trigger asynchronous
OSR exits. If a test requires no OSR exits, that test should
requireOption("--usePollingTraps=true") so that the VMTraps will use its
polling implementation instead.

I've tested this with a full run of the JSC stress tests with a debug
build and saw 0 timeouts. I've also tested it with a contrived tests that
loops forever, and saw the expected timeout crash.

Will look into re-tuning needed timeout value (and other JSC tests timeout
cleanup) in https://bugs.webkit.org/show_bug.cgi?id=205298.

  • interpreter/Interpreter.cpp:

(JSC::Interpreter::executeProgram):
(JSC::Interpreter::executeCall):
(JSC::Interpreter::executeConstruct):
(JSC::Interpreter::execute):
(JSC::Interpreter::executeModuleProgram):

  • interpreter/InterpreterInlines.h:

(JSC::Interpreter::execute):

  • jsc.cpp:

(timeoutCheckCallback):
(initializeTimeoutIfNeeded):
(startTimeoutThreadIfNeeded):
(runJSC):
(jscmain):

  • runtime/JSCConfig.h:
  • runtime/VM.h:

(JSC::VM::notifyNeedShellTimeoutCheck):

  • runtime/VMTraps.cpp:

(JSC::VMTraps::handleTraps):

  • runtime/VMTraps.h:

(JSC::VMTraps::Mask::Mask):
(JSC::VMTraps::Mask::allEventTypes):
(JSC::VMTraps::Mask::init):
(JSC::VMTraps::interruptingTraps):

  • tools/VMInspector.cpp:

(JSC::VMInspector::forEachVM):

  • tools/VMInspector.h:
Location:
trunk/Source/JavaScriptCore
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified trunk/Source/JavaScriptCore/ChangeLog

    r253576 r253581  
     12019-12-16  Mark Lam  <mark.lam@apple.com>
     2
     3        Changed jsc shell timeout mechanism to leverage the VMTraps and use CPUTime.
     4        https://bugs.webkit.org/show_bug.cgi?id=205279
     5        <rdar://problem/57971874>
     6
     7        Reviewed by Saam Barati.
     8
     9        This fixes all the timeouts that occur due to CPU time starvation when
     10        running JSC tests on a debug build.
     11
     12        What this means is that the timeout mechanism may trigger asynchronous
     13        OSR exits.  If a test requires no OSR exits, that test should
     14        requireOption("--usePollingTraps=true") so that the VMTraps will use its
     15        polling implementation instead.
     16
     17        I've tested this with a full run of the JSC stress tests with a debug
     18        build and saw 0 timeouts.  I've also tested it with a contrived tests that
     19        loops forever, and saw the expected timeout crash.
     20
     21        Will look into re-tuning needed timeout value (and other JSC tests timeout
     22        cleanup) in https://bugs.webkit.org/show_bug.cgi?id=205298.
     23
     24        * interpreter/Interpreter.cpp:
     25        (JSC::Interpreter::executeProgram):
     26        (JSC::Interpreter::executeCall):
     27        (JSC::Interpreter::executeConstruct):
     28        (JSC::Interpreter::execute):
     29        (JSC::Interpreter::executeModuleProgram):
     30        * interpreter/InterpreterInlines.h:
     31        (JSC::Interpreter::execute):
     32        * jsc.cpp:
     33        (timeoutCheckCallback):
     34        (initializeTimeoutIfNeeded):
     35        (startTimeoutThreadIfNeeded):
     36        (runJSC):
     37        (jscmain):
     38        * runtime/JSCConfig.h:
     39        * runtime/VM.h:
     40        (JSC::VM::notifyNeedShellTimeoutCheck):
     41        * runtime/VMTraps.cpp:
     42        (JSC::VMTraps::handleTraps):
     43        * runtime/VMTraps.h:
     44        (JSC::VMTraps::Mask::Mask):
     45        (JSC::VMTraps::Mask::allEventTypes):
     46        (JSC::VMTraps::Mask::init):
     47        (JSC::VMTraps::interruptingTraps):
     48        * tools/VMInspector.cpp:
     49        (JSC::VMInspector::forEachVM):
     50        * tools/VMInspector.h:
     51
    1522019-12-16  Yusuke Suzuki  <ysuzuki@apple.com>
    253
  • TabularUnified trunk/Source/JavaScriptCore/interpreter/Interpreter.cpp

    r253520 r253581  
    821821    }
    822822
    823     VMTraps::Mask mask(VMTraps::NeedTermination, VMTraps::NeedWatchdogCheck);
    824     if (UNLIKELY(vm.needTrapHandling(mask))) {
    825         vm.handleTraps(globalObject, vm.topCallFrame, mask);
     823    constexpr auto trapsMask = VMTraps::interruptingTraps();
     824    if (UNLIKELY(vm.needTrapHandling(trapsMask))) {
     825        vm.handleTraps(globalObject, vm.topCallFrame, trapsMask);
    826826        RETURN_IF_EXCEPTION(throwScope, throwScope.exception());
    827827    }
     
    882882        newCodeBlock = 0;
    883883
    884     VMTraps::Mask mask(VMTraps::NeedTermination, VMTraps::NeedWatchdogCheck);
    885     if (UNLIKELY(vm.needTrapHandling(mask))) {
    886         vm.handleTraps(globalObject, vm.topCallFrame, mask);
     884    constexpr auto trapsMask = VMTraps::interruptingTraps();
     885    if (UNLIKELY(vm.needTrapHandling(trapsMask))) {
     886        vm.handleTraps(globalObject, vm.topCallFrame, trapsMask);
    887887        RETURN_IF_EXCEPTION(throwScope, throwScope.exception());
    888888    }
     
    953953        newCodeBlock = 0;
    954954
    955     VMTraps::Mask mask(VMTraps::NeedTermination, VMTraps::NeedWatchdogCheck);
    956     if (UNLIKELY(vm.needTrapHandling(mask))) {
    957         vm.handleTraps(globalObject, vm.topCallFrame, mask);
     955    constexpr auto trapsMask = VMTraps::interruptingTraps();
     956    if (UNLIKELY(vm.needTrapHandling(trapsMask))) {
     957        vm.handleTraps(globalObject, vm.topCallFrame, trapsMask);
    958958        RETURN_IF_EXCEPTION(throwScope, nullptr);
    959959    }
     
    11371137    }
    11381138
    1139     VMTraps::Mask mask(VMTraps::NeedTermination, VMTraps::NeedWatchdogCheck);
    1140     if (UNLIKELY(vm.needTrapHandling(mask))) {
    1141         vm.handleTraps(globalObject, vm.topCallFrame, mask);
     1139    constexpr auto trapsMask = VMTraps::interruptingTraps();
     1140    if (UNLIKELY(vm.needTrapHandling(trapsMask))) {
     1141        vm.handleTraps(globalObject, vm.topCallFrame, trapsMask);
    11421142        RETURN_IF_EXCEPTION(throwScope, throwScope.exception());
    11431143    }
     
    11871187    }
    11881188
    1189     VMTraps::Mask mask(VMTraps::NeedTermination, VMTraps::NeedWatchdogCheck);
    1190     if (UNLIKELY(vm.needTrapHandling(mask))) {
    1191         vm.handleTraps(globalObject, vm.topCallFrame, mask);
     1189    constexpr auto trapsMask = VMTraps::interruptingTraps();
     1190    if (UNLIKELY(vm.needTrapHandling(trapsMask))) {
     1191        vm.handleTraps(globalObject, vm.topCallFrame, trapsMask);
    11921192        RETURN_IF_EXCEPTION(throwScope, throwScope.exception());
    11931193    }
  • TabularUnified trunk/Source/JavaScriptCore/interpreter/InterpreterInlines.h

    r251425 r253581  
    11/*
    22 * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>
    3  * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
     3 * Copyright (C) 2016-2019 Apple Inc. All rights reserved.
    44 *
    55 * Redistribution and use in source and binary forms, with or without
     
    7575    StackStats::CheckPoint stackCheckPoint;
    7676
    77     VMTraps::Mask mask(VMTraps::NeedTermination, VMTraps::NeedWatchdogCheck);
    78     if (UNLIKELY(vm.needTrapHandling(mask))) {
    79         vm.handleTraps(closure.protoCallFrame->globalObject, closure.oldCallFrame, mask);
     77    constexpr auto trapsMask = VMTraps::interruptingTraps();
     78    if (UNLIKELY(vm.needTrapHandling(trapsMask))) {
     79        vm.handleTraps(closure.protoCallFrame->globalObject, closure.oldCallFrame, trapsMask);
    8080        RETURN_IF_EXCEPTION(throwScope, throwScope.exception());
    8181    }
  • TabularUnified trunk/Source/JavaScriptCore/jsc.cpp

    r253458 r253581  
    7171#include "TestRunnerUtils.h"
    7272#include "TypedArrayInlines.h"
     73#include "VMInspector.h"
    7374#include "WasmCapabilities.h"
    7475#include "WasmContext.h"
     
    8586#include <type_traits>
    8687#include <wtf/Box.h>
     88#include <wtf/CPUTime.h>
    8789#include <wtf/CommaPrinter.h>
    8890#include <wtf/FileSystem.h>
     
    24282430static double s_desiredTimeout;
    24292431static double s_timeoutMultiplier = 1.0;
    2430 
    2431 static void startTimeoutThreadIfNeeded()
     2432static Seconds s_timeoutDuration;
     2433static Seconds s_maxAllowedCPUTime;
     2434static VM* s_vm;
     2435
     2436static void startTimeoutTimer(Seconds duration)
     2437{
     2438    Thread::create("jsc Timeout Thread", [=] () {
     2439        sleep(duration);
     2440        VMInspector::forEachVM([&] (VM& vm) -> VMInspector::FunctorStatus {
     2441            if (&vm != s_vm)
     2442                return VMInspector::FunctorStatus::Continue;
     2443            vm.notifyNeedShellTimeoutCheck();
     2444            return VMInspector::FunctorStatus::Done;
     2445        });
     2446    });
     2447}
     2448
     2449static void timeoutCheckCallback(VM& vm)
     2450{
     2451    RELEASE_ASSERT(&vm == s_vm);
     2452    auto cpuTime = CPUTime::forCurrentThread();
     2453    if (cpuTime >= s_maxAllowedCPUTime) {
     2454        dataLog("Timed out after ", s_timeoutDuration, " seconds!\n");
     2455        CRASH();
     2456    }
     2457    auto remainingTime = s_maxAllowedCPUTime - cpuTime;
     2458    startTimeoutTimer(remainingTime);
     2459}
     2460
     2461static void initializeTimeoutIfNeeded()
    24322462{
    24332463    if (char* timeoutString = getenv("JSCTEST_timeout")) {
     
    24352465            dataLog("WARNING: timeout string is malformed, got ", timeoutString,
    24362466                " but expected a number. Not using a timeout.\n");
    2437         } else {
    2438             Thread::create("jsc Timeout Thread", [] () {
    2439                 Seconds timeoutDuration(s_desiredTimeout * s_timeoutMultiplier);
    2440                 sleep(timeoutDuration);
    2441                 dataLog("Timed out after ", timeoutDuration, " seconds!\n");
    2442                 CRASH();
    2443             });
    2444         }
    2445     }
     2467        } else
     2468            g_jscConfig.shellTimeoutCheckCallback = timeoutCheckCallback;
     2469    }
     2470}
     2471
     2472static void startTimeoutThreadIfNeeded(VM& vm)
     2473{
     2474    if (!g_jscConfig.shellTimeoutCheckCallback)
     2475        return;
     2476
     2477    s_vm = &vm;
     2478    s_timeoutDuration = Seconds(s_desiredTimeout * s_timeoutMultiplier);
     2479    s_maxAllowedCPUTime = CPUTime::forCurrentThread() + s_timeoutDuration;
     2480    Seconds timeoutDuration(s_desiredTimeout * s_timeoutMultiplier);
     2481    startTimeoutTimer(timeoutDuration);
    24462482}
    24472483
     
    30003036        JSLockHolder locker(vm);
    30013037
     3038        startTimeoutThreadIfNeeded(vm);
    30023039        if (options.m_profile && !vm.m_perBytecodeProfiler)
    30033040            vm.m_perBytecodeProfiler = makeUnique<Profiler::Database>(vm);
     
    31003137    // Initialize JSC before getting VM.
    31013138    JSC::initializeThreading();
    3102     startTimeoutThreadIfNeeded();
     3139    initializeTimeoutIfNeeded();
    31033140#if ENABLE(WEBASSEMBLY)
    31043141    JSC::Wasm::enableFastMemory();
  • TabularUnified trunk/Source/JavaScriptCore/runtime/JSCConfig.h

    r252557 r253581  
    3333class ExecutableAllocator;
    3434class FixedVMPoolExecutableAllocator;
     35class VM;
    3536
    3637#if CPU(ARM64) || PLATFORM(WATCHOS)
     
    8384
    8485            OptionsStorage options;
     86
     87            void (*shellTimeoutCheckCallback)(VM&);
    8588        };
    8689        char ensureSize[ConfigSizeToProtect];
  • TabularUnified trunk/Source/JavaScriptCore/runtime/VM.h

    r253576 r253581  
    10661066
    10671067    void notifyNeedDebuggerBreak() { m_traps.fireTrap(VMTraps::NeedDebuggerBreak); }
     1068    void notifyNeedShellTimeoutCheck() { m_traps.fireTrap(VMTraps::NeedShellTimeoutCheck); }
    10681069    void notifyNeedTermination() { m_traps.fireTrap(VMTraps::NeedTermination); }
    10691070    void notifyNeedWatchdogCheck() { m_traps.fireTrap(VMTraps::NeedWatchdogCheck); }
  • TabularUnified trunk/Source/JavaScriptCore/runtime/VMTraps.cpp

    r251690 r253581  
    358358            invalidateCodeBlocksOnStack(callFrame);
    359359            break;
    360                
     360
    361361        case NeedWatchdogCheck:
    362362            ASSERT(vm.watchdog());
     
    364364                continue;
    365365            FALLTHROUGH;
     366
     367        case NeedShellTimeoutCheck:
     368            RELEASE_ASSERT(g_jscConfig.shellTimeoutCheckCallback);
     369            g_jscConfig.shellTimeoutCheckCallback(vm);
     370            break;
    366371
    367372        case NeedTermination:
  • TabularUnified trunk/Source/JavaScriptCore/runtime/VMTraps.h

    r251425 r253581  
    11/*
    2  * Copyright (C) 2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2017-2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    5353        // Sorted in servicing priority order from highest to lowest.
    5454        NeedDebuggerBreak,
     55        NeedShellTimeoutCheck,
    5556        NeedTermination,
    5657        NeedWatchdogCheck,
     
    6263    public:
    6364        enum AllEventTypes { AllEventTypesTag };
    64         Mask(AllEventTypes)
     65        constexpr Mask(AllEventTypes)
    6566            : m_mask(std::numeric_limits<BitField>::max())
    6667        { }
    67         static Mask allEventTypes() { return Mask(AllEventTypesTag); }
     68        static constexpr Mask allEventTypes() { return Mask(AllEventTypesTag); }
     69
     70        constexpr Mask(const Mask&) = default;
     71        constexpr Mask(Mask&&) = default;
    6872
    6973        template<typename... Arguments>
    70         Mask(Arguments... args)
     74        constexpr Mask(Arguments... args)
    7175            : m_mask(0)
    7276        {
     
    7882    private:
    7983        template<typename... Arguments>
    80         void init(EventType eventType, Arguments... args)
     84        constexpr void init(EventType eventType, Arguments... args)
    8185        {
    8286            ASSERT(eventType < NumberOfEventTypes);
     
    8589        }
    8690
    87         void init() { }
     91        constexpr void init() { }
    8892
    8993        BitField m_mask;
    9094    };
     95
     96    static constexpr Mask interruptingTraps() { return Mask(NeedShellTimeoutCheck, NeedTermination, NeedWatchdogCheck); }
    9197
    9298    ~VMTraps();
  • TabularUnified trunk/Source/JavaScriptCore/tools/VMInspector.cpp

    r252302 r253581  
    107107#endif // ENABLE(JIT)
    108108
     109void VMInspector::forEachVM(Function<FunctorStatus(VM&)>&& func)
     110{
     111    VMInspector& inspector = instance();
     112    Locker lock(inspector.getLock());
     113    inspector.iterate(func);
     114}
     115
    109116auto VMInspector::isValidExecutableMemory(const VMInspector::Locker&, void* machinePC) -> Expected<bool, Error>
    110117{
  • TabularUnified trunk/Source/JavaScriptCore/tools/VMInspector.h

    r251425 r253581  
    6161    void iterate(const Locker&, const Functor& functor) { iterate(functor); }
    6262
     63    JS_EXPORT_PRIVATE static void forEachVM(Function<FunctorStatus(VM&)>&&);
     64
    6365    Expected<Locker, Error> lock(Seconds timeout = Seconds::infinity());
    6466
Note: See TracChangeset for help on using the changeset viewer.