Changeset 149089 in webkit


Ignore:
Timestamp:
Apr 24, 2013 7:59:51 PM (11 years ago)
Author:
mark.lam@apple.com
Message:

Add watchdog timer polling for the DFG.
https://bugs.webkit.org/show_bug.cgi?id=115134.

Reviewed by Geoffrey Garen.

The strategy is to add a speculation check to the DFG generated code to
test if the watchdog timer has fired or not. If the watchdog timer has
fired, the generated code will do an OSR exit to the baseline JIT, and
let it handle servicing the watchdog timer.

If the watchdog is not enabled, this speculation check will not be
emitted.

  • API/tests/testapi.c:

(currentCPUTime_callAsFunction):
(extendTerminateCallback):
(main):

  • removed try/catch statements so that we can test the watchdog on the DFG.
  • added JS bindings to a native currentCPUTime() function so that the timeout tests can be more accurate.
  • also shortened the time values so that the tests can complete sooner.
  • bytecode/ExitKind.h:
  • dfg/DFGAbstractState.cpp:

(JSC::DFG::AbstractState::executeEffects):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::FixupPhase::fixupNode):

  • dfg/DFGNodeType.h:
  • dfg/DFGPredictionPropagationPhase.cpp:

(JSC::DFG::PredictionPropagationPhase::propagate):

  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • runtime/Watchdog.cpp:

(JSC::Watchdog::setTimeLimit):

Location:
trunk/Source/JavaScriptCore
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/tests/testapi.c

    r148639 r149089  
    10691069}
    10701070
     1071static JSValueRef currentCPUTime_callAsFunction(JSContextRef ctx, JSObjectRef functionObject, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
     1072{
     1073    UNUSED_PARAM(functionObject);
     1074    UNUSED_PARAM(thisObject);
     1075    UNUSED_PARAM(argumentCount);
     1076    UNUSED_PARAM(arguments);
     1077    UNUSED_PARAM(exception);
     1078
     1079    ASSERT(JSContextGetGlobalContext(ctx) == context);
     1080    return JSValueMakeNumber(ctx, currentCPUTime());
     1081}
     1082
    10711083bool shouldTerminateCallbackWasCalled = false;
    10721084static bool shouldTerminateCallback(JSContextRef ctx, void* context)
     
    10941106    if (extendTerminateCallbackCalled == 1) {
    10951107        JSContextGroupRef contextGroup = JSContextGetGroup(ctx);
    1096         JSContextGroupSetExecutionTimeLimit(contextGroup, 2, extendTerminateCallback, 0);
     1108        JSContextGroupSetExecutionTimeLimit(contextGroup, .200f, extendTerminateCallback, 0);
    10971109        return false;
    10981110    }
     
    17501762
    17511763#if PLATFORM(MAC) || PLATFORM(IOS)
     1764    JSStringRef currentCPUTimeStr = JSStringCreateWithUTF8CString("currentCPUTime");
     1765    JSObjectRef currentCPUTimeFunction = JSObjectMakeFunctionWithCallback(context, currentCPUTimeStr, currentCPUTime_callAsFunction);
     1766    JSObjectSetProperty(context, globalObject, currentCPUTimeStr, currentCPUTimeFunction, kJSPropertyAttributeNone, NULL);
     1767    JSStringRelease(currentCPUTimeStr);
     1768
    17521769    /* Test script timeout: */
    1753     JSContextGroupSetExecutionTimeLimit(contextGroup, 1, shouldTerminateCallback, 0);
     1770    JSContextGroupSetExecutionTimeLimit(contextGroup, .10f, shouldTerminateCallback, 0);
    17541771    {
    1755         const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
     1772        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
     1773        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
     1774        double startTime;
     1775        double endTime;
     1776        exception = NULL;
     1777        shouldTerminateCallbackWasCalled = false;
     1778        startTime = currentCPUTime();
     1779        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
     1780        endTime = currentCPUTime();
     1781
     1782        if (((endTime - startTime) < .150f) && shouldTerminateCallbackWasCalled)
     1783            printf("PASS: script timed out as expected.\n");
     1784        else {
     1785            if (!((endTime - startTime) < .150f))
     1786                printf("FAIL: script did not timed out as expected.\n");
     1787            if (!shouldTerminateCallbackWasCalled)
     1788                printf("FAIL: script timeout callback was not called.\n");
     1789            failed = true;
     1790        }
     1791
     1792        if (!exception) {
     1793            printf("FAIL: TerminationExecutionException was not thrown.\n");
     1794            failed = true;
     1795        }
     1796    }
     1797
     1798    /* Test the script timeout's TerminationExecutionException should NOT be catchable: */
     1799    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, shouldTerminateCallback, 0);
     1800    {
     1801        const char* loopForeverScript = "var startTime = currentCPUTime(); try { while (true) { if (currentCPUTime() - startTime > .150) break; } } catch(e) { }";
     1802        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
     1803        double startTime;
     1804        double endTime;
     1805        exception = NULL;
     1806        shouldTerminateCallbackWasCalled = false;
     1807        startTime = currentCPUTime();
     1808        v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
     1809        endTime = currentCPUTime();
     1810
     1811        if (((endTime - startTime) >= .150f) || !shouldTerminateCallbackWasCalled) {
     1812            if (!((endTime - startTime) < .150f))
     1813                printf("FAIL: script did not timed out as expected.\n");
     1814            if (!shouldTerminateCallbackWasCalled)
     1815                printf("FAIL: script timeout callback was not called.\n");
     1816            failed = true;
     1817        }
     1818
     1819        if (exception)
     1820            printf("PASS: TerminationExecutionException was not catchable as expected.\n");
     1821        else {
     1822            printf("FAIL: TerminationExecutionException was caught.\n");
     1823            failed = true;
     1824        }
     1825    }
     1826
     1827    /* Test script timeout cancellation: */
     1828    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.10f, cancelTerminateCallback, 0);
     1829    {
     1830        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .150) break; } ";
    17561831        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
    17571832        double startTime;
     
    17621837        endTime = currentCPUTime();
    17631838
    1764         if (((endTime - startTime) < 2) && shouldTerminateCallbackWasCalled)
    1765             printf("PASS: script timed out as expected.\n");
    1766         else {
    1767             if (!((endTime - startTime) < 2))
    1768                 printf("FAIL: script did not timed out as expected.\n");
    1769             if (!shouldTerminateCallbackWasCalled)
    1770                 printf("FAIL: script timeout callback was not called.\n");
    1771             failed = true;
    1772         }
    1773 
    1774         if (exception)
    1775             printf("PASS: TerminationExecutionException was not catchable.\n");
    1776         else {
    1777             printf("FAIL: TerminationExecutionException was caught.\n");
    1778             failed = true;
    1779         }
    1780     }
    1781 
    1782     /* Test script timeout cancellation: */
    1783     JSContextGroupSetExecutionTimeLimit(contextGroup, 1, cancelTerminateCallback, 0);
    1784     {
    1785         const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
    1786         JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
    1787         double startTime;
    1788         double endTime;
    1789         exception = NULL;
    1790         startTime = currentCPUTime();
    1791         v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
    1792         endTime = currentCPUTime();
    1793 
    1794         if (((endTime - startTime) >= 2) && cancelTerminateCallbackWasCalled && !exception)
     1839        if (((endTime - startTime) >= .150f) && cancelTerminateCallbackWasCalled && !exception)
    17951840            printf("PASS: script timeout was cancelled as expected.\n");
    17961841        else {
    1797             if (((endTime - startTime) < 2) || exception)
     1842            if (((endTime - startTime) < .150) || exception)
    17981843                printf("FAIL: script timeout was not cancelled.\n");
    17991844            if (!cancelTerminateCallbackWasCalled)
     
    18041849
    18051850    /* Test script timeout extension: */
    1806     JSContextGroupSetExecutionTimeLimit(contextGroup, 1, extendTerminateCallback, 0);
     1851    JSContextGroupSetExecutionTimeLimit(contextGroup, 0.100f, extendTerminateCallback, 0);
    18071852    {
    1808         const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
     1853        const char* loopForeverScript = "var startTime = currentCPUTime(); while (true) { if (currentCPUTime() - startTime > .500) break; } ";
    18091854        JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
    18101855        double startTime;
     
    18171862        deltaTime = endTime - startTime;
    18181863
    1819         if ((deltaTime >= 3) && (deltaTime < 5) && (extendTerminateCallbackCalled == 2) && exception)
     1864        if ((deltaTime >= .300f) && (deltaTime < .500f) && (extendTerminateCallbackCalled == 2) && exception)
    18201865            printf("PASS: script timeout was extended as expected.\n");
    18211866        else {
    1822             if (deltaTime < 2)
     1867            if (deltaTime < .200f)
    18231868                printf("FAIL: script timeout was not extended as expected.\n");
    1824             else if (deltaTime >= 5)
     1869            else if (deltaTime >= .500f)
    18251870                printf("FAIL: script did not timeout.\n");
    18261871
     
    18311876
    18321877            if (!exception)
    1833                 printf("FAIL: TerminationExecutionException was caught during timeout extension test.\n");
     1878                printf("FAIL: TerminationExecutionException was not thrown during timeout extension test.\n");
    18341879
    18351880            failed = true;
  • trunk/Source/JavaScriptCore/ChangeLog

    r149082 r149089  
     12013-04-24  Mark Lam  <mark.lam@apple.com>
     2
     3        Add watchdog timer polling for the DFG.
     4        https://bugs.webkit.org/show_bug.cgi?id=115134.
     5
     6        Reviewed by Geoffrey Garen.
     7
     8        The strategy is to add a speculation check to the DFG generated code to
     9        test if the watchdog timer has fired or not. If the watchdog timer has
     10        fired, the generated code will do an OSR exit to the baseline JIT, and
     11        let it handle servicing the watchdog timer.
     12
     13        If the watchdog is not enabled, this speculation check will not be
     14        emitted.
     15
     16        * API/tests/testapi.c:
     17        (currentCPUTime_callAsFunction):
     18        (extendTerminateCallback):
     19        (main):
     20        - removed try/catch statements so that we can test the watchdog on the DFG.
     21        - added JS bindings to a native currentCPUTime() function so that the timeout
     22          tests can be more accurate.
     23        - also shortened the time values so that the tests can complete sooner.
     24
     25        * bytecode/ExitKind.h:
     26        * dfg/DFGAbstractState.cpp:
     27        (JSC::DFG::AbstractState::executeEffects):
     28        * dfg/DFGByteCodeParser.cpp:
     29        (JSC::DFG::ByteCodeParser::parseBlock):
     30        * dfg/DFGFixupPhase.cpp:
     31        (JSC::DFG::FixupPhase::fixupNode):
     32        * dfg/DFGNodeType.h:
     33        * dfg/DFGPredictionPropagationPhase.cpp:
     34        (JSC::DFG::PredictionPropagationPhase::propagate):
     35        * dfg/DFGSpeculativeJIT32_64.cpp:
     36        (JSC::DFG::SpeculativeJIT::compile):
     37        * dfg/DFGSpeculativeJIT64.cpp:
     38        (JSC::DFG::SpeculativeJIT::compile):
     39        * runtime/Watchdog.cpp:
     40        (JSC::Watchdog::setTimeLimit):
     41
    1422013-04-24  Filip Pizlo  <fpizlo@apple.com>
    243
  • trunk/Source/JavaScriptCore/bytecode/ExitKind.h

    r146089 r149089  
    4747    NotStringObject, // We exited because we shouldn't have attempted to optimize string object access.
    4848    Uncountable, // We exited for none of the above reasons, and we should not count it. Most uses of this should be viewed as a FIXME.
    49     UncountableWatchpoint // We exited because of a watchpoint, which isn't counted because watchpoints do tracking themselves.
     49    UncountableWatchpoint, // We exited because of a watchpoint, which isn't counted because watchpoints do tracking themselves.
     50    WatchdogTimerFired // We exited because we need to service the watchdog timer.
    5051};
    5152
  • trunk/Source/JavaScriptCore/dfg/DFGAbstractState.cpp

    r149016 r149089  
    15391539        break;
    15401540           
     1541    case CheckWatchdogTimer:
     1542        node->setCanExit(true);
     1543        break;
     1544           
    15411545    case Phantom:
    15421546    case InlineStart:
  • trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp

    r149071 r149089  
    32863286                m_currentBlock->isOSRTarget = true;
    32873287
    3288             // Emit a phantom node to ensure that there is a placeholder node for this bytecode
    3289             // op.
    3290             addToGraph(Phantom);
     3288            if (m_vm->watchdog.isEnabled())
     3289                addToGraph(CheckWatchdogTimer);
     3290            else {
     3291                // Emit a phantom node to ensure that there is a placeholder
     3292                // node for this bytecode op.
     3293                addToGraph(Phantom);
     3294            }
    32913295           
    32923296            NEXT_OPCODE(op_loop_hint);
  • trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp

    r149041 r149089  
    860860        case CountExecution:
    861861        case ForceOSRExit:
     862        case CheckWatchdogTimer:
    862863            break;
    863864#else
  • trunk/Source/JavaScriptCore/dfg/DFGNodeType.h

    r147985 r149089  
    265265    /* this point, but execution does continue in the basic block - just in a */\
    266266    /* different compiler. */\
    267     macro(ForceOSRExit, NodeMustGenerate)
     267    macro(ForceOSRExit, NodeMustGenerate) \
     268    \
     269    /* Checks the watchdog timer. If the timer has fired, we OSR exit to the */ \
     270    /* baseline JIT to redo the watchdog timer check, and service the timer. */ \
     271    macro(CheckWatchdogTimer, NodeMustGenerate) \
    268272
    269273// This enum generates a monotonically increasing id for all Node types,
  • trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp

    r147985 r149089  
    527527        case PutGlobalVar:
    528528        case PutGlobalVarCheck:
     529        case CheckWatchdogTimer:
    529530            break;
    530531           
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp

    r148696 r149089  
    49274927    }
    49284928
     4929    case CheckWatchdogTimer:
     4930        speculationCheck(
     4931            WatchdogTimerFired, JSValueRegs(), 0,
     4932            m_jit.branchTest8(
     4933                JITCompiler::NonZero,
     4934                JITCompiler::AbsoluteAddress(m_jit.vm()->watchdog.timerDidFireAddress())));
     4935        break;
     4936
    49294937    case CountExecution:
    49304938        m_jit.add64(TrustedImm32(1), MacroAssembler::AbsoluteAddress(node->executionCounter()->address()));
  • trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp

    r148696 r149089  
    47714771    }
    47724772
     4773    case CheckWatchdogTimer:
     4774        speculationCheck(
     4775            WatchdogTimerFired, JSValueRegs(), 0,
     4776            m_jit.branchTest8(
     4777                JITCompiler::NonZero,
     4778                JITCompiler::AbsoluteAddress(m_jit.vm()->watchdog.timerDidFireAddress())));
     4779        break;
     4780
    47734781    case Phantom:
    47744782        DFG_NODE_DO_TO_CHILDREN(m_jit.graph(), node, speculate);
  • trunk/Source/JavaScriptCore/runtime/Watchdog.cpp

    r148696 r149089  
    8080    // polling checks. Hence, there is no need to re-do this flushing.
    8181    if (!wasEnabled) {
    82         // For now, we only support this feature when the DFG JIT is disabled.
    83         Options::useDFGJIT() = false;
    84 
    85         // And if we've previously compiled any functions, we need to deopt
    86         // them because they don't habe the needed polling checks yet.
     82        // And if we've previously compiled any functions, we need to revert
     83        // them because they don't have the needed polling checks yet.
    8784        vm.releaseExecutableMemory();
    8885    }
Note: See TracChangeset for help on using the changeset viewer.