<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[183974] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/183974">183974</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2015-05-07 19:12:35 -0700 (Thu, 07 May 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>GC has trouble with pathologically large array allocations
https://bugs.webkit.org/show_bug.cgi?id=144609

Reviewed by Geoffrey Garen.
Source/JavaScriptCore:


The bug was that SlotVisitor::copyLater() would return early for oversize blocks (right
after pinning them), and would skip the accounting. The GC calculates the size of the heap
in tandem with the scan to save time, and that accounting was part of how the GC would
know how big the heap was. The GC would then think that oversize copied blocks use no
memory, and would then mess up its scheduling of the next GC.
        
Fixing this bug is harder than it seems. When running an eden GC, we figure out the heap
size by summing the size from the last collection and the size by walking the eden heap.
But this breaks when we eagerly delete objects that the last collection touched. We can do
that in one corner case: copied block reallocation. The old block will be deleted from old
space during the realloc and a new block will be allocated in new space. In order for the
GC to know that the size of old space actually shrank, we need a field to tell us how much
such shrinkage could occur. Since this is a very dirty corner case and it only works for
very particular reasons arising from the special properties of copied space (single owner,
and the realloc is used in places where the compiler already knows that it cannot register
allocate a pointer to the old block), I opted for an equally dirty shrinkage counter
devoted just to this case. It's called bytesRemovedFromOldSpaceDueToReallocation.
        
To test this, I needed to add an Option to force a particular RAM size in the GC. This
allows us to write tests that assert that the GC heap size is some value X, without
worrying about machine-to-machine variations due to GC heuristics changing based on RAM
size.

* heap/CopiedSpace.cpp:
(JSC::CopiedSpace::CopiedSpace): Initialize the dirty shrinkage counter.
(JSC::CopiedSpace::tryReallocateOversize): Bump the dirty shrinkage counter.
* heap/CopiedSpace.h:
(JSC::CopiedSpace::takeBytesRemovedFromOldSpaceDueToReallocation): Swap out the counter. Used by the GC when it does its accounting.
* heap/Heap.cpp:
(JSC::Heap::Heap): Allow the user to force the RAM size.
(JSC::Heap::updateObjectCounts): Use the dirty shrinkage counter to good effect. Also, make this code less confusing.
* heap/SlotVisitorInlines.h:
(JSC::SlotVisitor::copyLater): The early return for isOversize() was the bug. We still need to report these bytes as live. Otherwise the GC doesn't know that it owns this memory.
* jsc.cpp: Add size measuring hooks to write the largeish test.
(GlobalObject::finishCreation):
(functionGCAndSweep):
(functionFullGC):
(functionEdenGC):
(functionHeapSize):
* runtime/Options.h:
* tests/stress/new-array-storage-array-with-size.js: Fix this so that it actually allocates ArrayStorage arrays and tests the thing it was supposed to test.
* tests/stress/new-largeish-contiguous-array-with-size.js: Added. This tests what the other test accidentally started testing, but does so without running your system out of memory.
(foo):
(test):

Tools:

        
Add a --filter option that restricts the set of tests we run. I needed it to fix this bug
and it's a frequently requested feature.
        
Also add the ability to run a test pretending that your system has a particular RAM size.
This is useful for GC tests, and the new GC test that I added uses this.

* Scripts/run-javascriptcore-tests:
(runJSCStressTests):
* Scripts/run-jsc-stress-tests:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapCopiedSpacecpp">trunk/Source/JavaScriptCore/heap/CopiedSpace.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapCopiedSpaceh">trunk/Source/JavaScriptCore/heap/CopiedSpace.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapcpp">trunk/Source/JavaScriptCore/heap/Heap.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapSlotVisitorInlinesh">trunk/Source/JavaScriptCore/heap/SlotVisitorInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCorejsccpp">trunk/Source/JavaScriptCore/jsc.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeOptionsh">trunk/Source/JavaScriptCore/runtime/Options.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstressnewarraystoragearraywithsizejs">trunk/Source/JavaScriptCore/tests/stress/new-array-storage-array-with-size.js</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsScriptsrunjavascriptcoretests">trunk/Tools/Scripts/run-javascriptcore-tests</a></li>
<li><a href="#trunkToolsScriptsrunjscstresstests">trunk/Tools/Scripts/run-jsc-stress-tests</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoretestsstressnewlargeishcontiguousarraywithsizejs">trunk/Source/JavaScriptCore/tests/stress/new-largeish-contiguous-array-with-size.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -1,3 +1,55 @@
</span><ins>+2015-05-07  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        GC has trouble with pathologically large array allocations
+        https://bugs.webkit.org/show_bug.cgi?id=144609
+
+        Reviewed by Geoffrey Garen.
+
+        The bug was that SlotVisitor::copyLater() would return early for oversize blocks (right
+        after pinning them), and would skip the accounting. The GC calculates the size of the heap
+        in tandem with the scan to save time, and that accounting was part of how the GC would
+        know how big the heap was. The GC would then think that oversize copied blocks use no
+        memory, and would then mess up its scheduling of the next GC.
+        
+        Fixing this bug is harder than it seems. When running an eden GC, we figure out the heap
+        size by summing the size from the last collection and the size by walking the eden heap.
+        But this breaks when we eagerly delete objects that the last collection touched. We can do
+        that in one corner case: copied block reallocation. The old block will be deleted from old
+        space during the realloc and a new block will be allocated in new space. In order for the
+        GC to know that the size of old space actually shrank, we need a field to tell us how much
+        such shrinkage could occur. Since this is a very dirty corner case and it only works for
+        very particular reasons arising from the special properties of copied space (single owner,
+        and the realloc is used in places where the compiler already knows that it cannot register
+        allocate a pointer to the old block), I opted for an equally dirty shrinkage counter
+        devoted just to this case. It's called bytesRemovedFromOldSpaceDueToReallocation.
+        
+        To test this, I needed to add an Option to force a particular RAM size in the GC. This
+        allows us to write tests that assert that the GC heap size is some value X, without
+        worrying about machine-to-machine variations due to GC heuristics changing based on RAM
+        size.
+
+        * heap/CopiedSpace.cpp:
+        (JSC::CopiedSpace::CopiedSpace): Initialize the dirty shrinkage counter.
+        (JSC::CopiedSpace::tryReallocateOversize): Bump the dirty shrinkage counter.
+        * heap/CopiedSpace.h:
+        (JSC::CopiedSpace::takeBytesRemovedFromOldSpaceDueToReallocation): Swap out the counter. Used by the GC when it does its accounting.
+        * heap/Heap.cpp:
+        (JSC::Heap::Heap): Allow the user to force the RAM size.
+        (JSC::Heap::updateObjectCounts): Use the dirty shrinkage counter to good effect. Also, make this code less confusing.
+        * heap/SlotVisitorInlines.h:
+        (JSC::SlotVisitor::copyLater): The early return for isOversize() was the bug. We still need to report these bytes as live. Otherwise the GC doesn't know that it owns this memory.
+        * jsc.cpp: Add size measuring hooks to write the largeish test.
+        (GlobalObject::finishCreation):
+        (functionGCAndSweep):
+        (functionFullGC):
+        (functionEdenGC):
+        (functionHeapSize):
+        * runtime/Options.h:
+        * tests/stress/new-array-storage-array-with-size.js: Fix this so that it actually allocates ArrayStorage arrays and tests the thing it was supposed to test.
+        * tests/stress/new-largeish-contiguous-array-with-size.js: Added. This tests what the other test accidentally started testing, but does so without running your system out of memory.
+        (foo):
+        (test):
+
</ins><span class="cx"> 2015-05-07  Saam Barati  &lt;saambarati1@gmail.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Global functions should be initialized as JSFunctions in byte code
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapCopiedSpacecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/CopiedSpace.cpp (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/CopiedSpace.cpp        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Source/JavaScriptCore/heap/CopiedSpace.cpp        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -38,6 +38,7 @@
</span><span class="cx">     , m_inCopyingPhase(false)
</span><span class="cx">     , m_shouldDoCopyPhase(false)
</span><span class="cx">     , m_numberOfLoanedBlocks(0)
</span><ins>+    , m_bytesRemovedFromOldSpaceDueToReallocation(0)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -155,9 +156,13 @@
</span><span class="cx"> 
</span><span class="cx">     CopiedBlock* oldBlock = CopiedSpace::blockFor(oldPtr);
</span><span class="cx">     if (oldBlock-&gt;isOversize()) {
</span><del>-        if (oldBlock-&gt;isOld())
</del><ins>+        // FIXME: Eagerly deallocating the old space block probably buys more confusion than
+        // value.
+        // https://bugs.webkit.org/show_bug.cgi?id=144750
+        if (oldBlock-&gt;isOld()) {
+            m_bytesRemovedFromOldSpaceDueToReallocation += oldBlock-&gt;size();
</ins><span class="cx">             m_oldGen.oversizeBlocks.remove(oldBlock);
</span><del>-        else
</del><ins>+        } else
</ins><span class="cx">             m_newGen.oversizeBlocks.remove(oldBlock);
</span><span class="cx">         m_blockSet.remove(oldBlock);
</span><span class="cx">         CopiedBlock::destroy(oldBlock);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapCopiedSpaceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/CopiedSpace.h (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/CopiedSpace.h        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Source/JavaScriptCore/heap/CopiedSpace.h        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -86,6 +86,13 @@
</span><span class="cx">     static CopiedBlock* blockFor(void*);
</span><span class="cx"> 
</span><span class="cx">     Heap* heap() const { return m_heap; }
</span><ins>+    
+    size_t takeBytesRemovedFromOldSpaceDueToReallocation()
+    {
+        size_t result = 0;
+        std::swap(m_bytesRemovedFromOldSpaceDueToReallocation, result);
+        return result;
+    }
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     static bool isOversize(size_t);
</span><span class="lines">@@ -135,6 +142,8 @@
</span><span class="cx">     Mutex m_loanedBlocksLock; 
</span><span class="cx">     ThreadCondition m_loanedBlocksCondition;
</span><span class="cx">     size_t m_numberOfLoanedBlocks;
</span><ins>+    
+    size_t m_bytesRemovedFromOldSpaceDueToReallocation;
</ins><span class="cx"> 
</span><span class="cx">     static const size_t s_maxAllocationSize = CopiedBlock::blockSize / 2;
</span><span class="cx">     static const size_t s_initialBlockNum = 16;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/Heap.cpp (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/Heap.cpp        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Source/JavaScriptCore/heap/Heap.cpp        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -314,7 +314,7 @@
</span><span class="cx"> 
</span><span class="cx"> Heap::Heap(VM* vm, HeapType heapType)
</span><span class="cx">     : m_heapType(heapType)
</span><del>-    , m_ramSize(ramSize())
</del><ins>+    , m_ramSize(Options::forceRAMSize() ? Options::forceRAMSize() : ramSize())
</ins><span class="cx">     , m_minBytesPerCycle(minHeapSize(m_heapType, m_ramSize))
</span><span class="cx">     , m_sizeAfterLastCollect(0)
</span><span class="cx">     , m_sizeAfterLastFullCollect(0)
</span><span class="lines">@@ -818,15 +818,18 @@
</span><span class="cx"> #endif
</span><span class="cx">         dataLogF(&quot;\nNumber of live Objects after GC %lu, took %.6f secs\n&quot;, static_cast&lt;unsigned long&gt;(visitCount), WTF::monotonicallyIncreasingTime() - gcStartTime);
</span><span class="cx">     }
</span><del>-
-    if (m_operationInProgress == EdenCollection) {
-        m_totalBytesVisited += m_slotVisitor.bytesVisited();
-        m_totalBytesCopied += m_slotVisitor.bytesCopied();
-    } else {
-        ASSERT(m_operationInProgress == FullCollection);
-        m_totalBytesVisited = m_slotVisitor.bytesVisited();
-        m_totalBytesCopied = m_slotVisitor.bytesCopied();
-    }
</del><ins>+    
+    size_t bytesRemovedFromOldSpaceDueToReallocation =
+        m_storageSpace.takeBytesRemovedFromOldSpaceDueToReallocation();
+    
+    if (m_operationInProgress == FullCollection) {
+        m_totalBytesVisited = 0;
+        m_totalBytesCopied = 0;
+    } else
+        m_totalBytesCopied -= bytesRemovedFromOldSpaceDueToReallocation;
+    
+    m_totalBytesVisited += m_slotVisitor.bytesVisited();
+    m_totalBytesCopied += m_slotVisitor.bytesCopied();
</ins><span class="cx"> #if ENABLE(PARALLEL_GC)
</span><span class="cx">     m_totalBytesVisited += m_sharedData.childBytesVisited();
</span><span class="cx">     m_totalBytesCopied += m_sharedData.childBytesCopied();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapSlotVisitorInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/SlotVisitorInlines.h (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/SlotVisitorInlines.h        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Source/JavaScriptCore/heap/SlotVisitorInlines.h        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -239,8 +239,13 @@
</span><span class="cx">     ASSERT(bytes);
</span><span class="cx">     CopiedBlock* block = CopiedSpace::blockFor(ptr);
</span><span class="cx">     if (block-&gt;isOversize()) {
</span><ins>+        ASSERT(bytes &lt;= block-&gt;size());
+        // FIXME: We should be able to shrink the allocation if bytes went below the block size.
+        // For now, we just make sure that our accounting of how much memory we are actually using
+        // is correct.
+        // https://bugs.webkit.org/show_bug.cgi?id=144749
+        bytes = block-&gt;size();
</ins><span class="cx">         m_shared.m_copiedSpace-&gt;pin(block);
</span><del>-        return;
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     ASSERT(heap()-&gt;m_storageSpace.contains(block));
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejsccpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jsc.cpp (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jsc.cpp        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Source/JavaScriptCore/jsc.cpp        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -447,6 +447,7 @@
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionGCAndSweep(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionFullGC(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState*);
</span><ins>+static EncodedJSValue JSC_HOST_CALL functionHeapSize(ExecState*);
</ins><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionDeleteAllCompiledCode(ExecState*);
</span><span class="cx"> #ifndef NDEBUG
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
</span><span class="lines">@@ -586,6 +587,7 @@
</span><span class="cx">         addFunction(vm, &quot;gc&quot;, functionGCAndSweep, 0);
</span><span class="cx">         addFunction(vm, &quot;fullGC&quot;, functionFullGC, 0);
</span><span class="cx">         addFunction(vm, &quot;edenGC&quot;, functionEdenGC, 0);
</span><ins>+        addFunction(vm, &quot;gcHeapSize&quot;, functionHeapSize, 0);
</ins><span class="cx">         addFunction(vm, &quot;deleteAllCompiledCode&quot;, functionDeleteAllCompiledCode, 0);
</span><span class="cx"> #ifndef NDEBUG
</span><span class="cx">         addFunction(vm, &quot;dumpCallFrame&quot;, functionDumpCallFrame, 0);
</span><span class="lines">@@ -834,23 +836,29 @@
</span><span class="cx"> {
</span><span class="cx">     JSLockHolder lock(exec);
</span><span class="cx">     exec-&gt;heap()-&gt;collectAllGarbage();
</span><del>-    return JSValue::encode(jsUndefined());
</del><ins>+    return JSValue::encode(jsNumber(exec-&gt;heap()-&gt;sizeAfterLastFullCollection()));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL functionFullGC(ExecState* exec)
</span><span class="cx"> {
</span><span class="cx">     JSLockHolder lock(exec);
</span><span class="cx">     exec-&gt;heap()-&gt;collect(FullCollection);
</span><del>-    return JSValue::encode(jsUndefined());
</del><ins>+    return JSValue::encode(jsNumber(exec-&gt;heap()-&gt;sizeAfterLastFullCollection()));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL functionEdenGC(ExecState* exec)
</span><span class="cx"> {
</span><span class="cx">     JSLockHolder lock(exec);
</span><span class="cx">     exec-&gt;heap()-&gt;collect(EdenCollection);
</span><del>-    return JSValue::encode(jsUndefined());
</del><ins>+    return JSValue::encode(jsNumber(exec-&gt;heap()-&gt;sizeAfterLastEdenCollection()));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+EncodedJSValue JSC_HOST_CALL functionHeapSize(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    return JSValue::encode(jsNumber(exec-&gt;heap()-&gt;size()));
+}
+
</ins><span class="cx"> EncodedJSValue JSC_HOST_CALL functionDeleteAllCompiledCode(ExecState* exec)
</span><span class="cx"> {
</span><span class="cx">     JSLockHolder lock(exec);
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeOptionsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/Options.h (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/Options.h        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Source/JavaScriptCore/runtime/Options.h        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -291,6 +291,7 @@
</span><span class="cx">     v(gcLogLevel, logGC, GCLogging::None, &quot;debugging option to log GC activity (0 = None, 1 = Basic, 2 = Verbose)&quot;) \
</span><span class="cx">     v(bool, disableGC, false, nullptr) \
</span><span class="cx">     v(unsigned, gcMaxHeapSize, 0, nullptr) \
</span><ins>+    v(unsigned, forceRAMSize, 0, nullptr) \
</ins><span class="cx">     v(bool, recordGCPauseTimes, false, nullptr) \
</span><span class="cx">     v(bool, logHeapStatisticsAtExit, false, nullptr) \
</span><span class="cx">     v(bool, enableTypeProfiler, false, nullptr) \
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressnewarraystoragearraywithsizejs"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/stress/new-array-storage-array-with-size.js (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/new-array-storage-array-with-size.js        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Source/JavaScriptCore/tests/stress/new-array-storage-array-with-size.js        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -1,12 +1,15 @@
</span><del>-// https://bugs.webkit.org/show_bug.cgi?id=144609
-//@ skip
-
</del><span class="cx"> function foo(x) {
</span><span class="cx">     return new Array(x);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> noInline(foo);
</span><span class="cx"> 
</span><ins>+// Warm up up to create array storage.
+for (var i = 0; i &lt; 10000; ++i) {
+    var array = foo(10);
+    array.__defineSetter__(0, function(v) { });
+}
+
</ins><span class="cx"> function test(size) {
</span><span class="cx">     var result = foo(size);
</span><span class="cx">     if (result.length != size)
</span><span class="lines">@@ -22,5 +25,5 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> for (var i = 0; i &lt; 100000; ++i) {
</span><del>-    test(1000000);
</del><ins>+    test(10);
</ins><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstressnewlargeishcontiguousarraywithsizejs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/stress/new-largeish-contiguous-array-with-size.js (0 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/new-largeish-contiguous-array-with-size.js                                (rev 0)
+++ trunk/Source/JavaScriptCore/tests/stress/new-largeish-contiguous-array-with-size.js        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -0,0 +1,47 @@
</span><ins>+// We only need one run of this with any GC or JIT strategy. This test is not particularly fast.
+// Unfortunately, it needs to run for a while to test the thing it's testing.
+//@ slow!
+//@ runWithRAMSize(10000000)
+
+function foo(x) {
+    return new Array(x);
+}
+
+noInline(foo);
+
+function test(size) {
+    var result = foo(size);
+    if (result.length != size)
+        throw &quot;Error: bad result: &quot; + result;
+    var sawThings = false;
+    for (var s in result)
+        sawThings = true;
+    if (sawThings)
+        throw &quot;Error: array is in bad state: &quot; + result;
+    result[0] = &quot;42.5&quot;;
+    if (result[0] != &quot;42.5&quot;)
+        throw &quot;Error: array is in weird state: &quot; + result;
+}
+
+var result = gcHeapSize();
+
+for (var i = 0; i &lt; 1000; ++i) {
+    // The test was written when we found that large array allocations weren't being accounted for
+    // in that part of the GC's accounting that determined the GC trigger. Consequently, the GC
+    // would run too infrequently in this loop and we would use an absurd amount of memory when this
+    // loop exited.
+    test(50000);
+}
+
+// Last time I tested, the heap should be 3725734 before and 125782 after. I don't want to enforce
+// exactly that. If you regress the accounting code, the GC heap size at this point will be much
+// more than that.
+var result = gcHeapSize();
+if (result &gt; 10000000)
+    throw &quot;Error: heap too big before forced GC: &quot; + result;
+
+// Do a final check after GC, just for sanity.
+gc();
+result = gcHeapSize();
+if (result &gt; 1000000)
+    throw &quot;Error: heap too big after forced GC: &quot; + result;
</ins></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Tools/ChangeLog        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -1,3 +1,20 @@
</span><ins>+2015-05-07  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        GC has trouble with pathologically large array allocations
+        https://bugs.webkit.org/show_bug.cgi?id=144609
+
+        Reviewed by Geoffrey Garen.
+        
+        Add a --filter option that restricts the set of tests we run. I needed it to fix this bug
+        and it's a frequently requested feature.
+        
+        Also add the ability to run a test pretending that your system has a particular RAM size.
+        This is useful for GC tests, and the new GC test that I added uses this.
+
+        * Scripts/run-javascriptcore-tests:
+        (runJSCStressTests):
+        * Scripts/run-jsc-stress-tests:
+
</ins><span class="cx"> 2015-05-07  Csaba Osztrogonác  &lt;ossy@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         [EFL] Bump EFL version to 1.14.0
</span></span></pre></div>
<a id="trunkToolsScriptsrunjavascriptcoretests"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/run-javascriptcore-tests (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/run-javascriptcore-tests        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Tools/Scripts/run-javascriptcore-tests        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -66,6 +66,7 @@
</span><span class="cx"> my $buildJSCDefault = $buildJSC ? &quot;will check&quot; : &quot;will not check&quot;;
</span><span class="cx"> my $testapiDefault = $runTestAPI ? &quot;will run&quot; : &quot;will not run&quot;;
</span><span class="cx"> my $jscStressDefault = $runJSCStress ? &quot;will run&quot; : &quot; will not run&quot;;
</span><ins>+my $filter;
</ins><span class="cx"> my $usage = &lt;&lt;EOF;
</span><span class="cx"> Usage: $programName [options] [options to pass to build system]
</span><span class="cx">   --help                        Show this help message
</span><span class="lines">@@ -89,6 +90,7 @@
</span><span class="cx">   --shell-runner                Uses the shell-based test runner instead of the default make-based runner.
</span><span class="cx">                                 In general the shell runner is slower than the make runner.
</span><span class="cx">   --make-runner                 Uses the faster make-based runner.
</span><ins>+  --filter                      Only run tests whose name matches the given regular expression.
</ins><span class="cx"> 
</span><span class="cx"> EOF
</span><span class="cx"> 
</span><span class="lines">@@ -106,6 +108,7 @@
</span><span class="cx">     'child-processes=s' =&gt; \$childProcesses,
</span><span class="cx">     'shell-runner' =&gt; \$shellRunner,
</span><span class="cx">     'make-runner' =&gt; \$makeRunner,
</span><ins>+    'filter=s' =&gt; \$filter,
</ins><span class="cx">     'help' =&gt; \$showHelp
</span><span class="cx"> );
</span><span class="cx"> 
</span><span class="lines">@@ -313,6 +316,11 @@
</span><span class="cx">     if ($makeRunner) {
</span><span class="cx">         push(@jscStressDriverCmd, &quot;--make-runner&quot;);
</span><span class="cx">     }
</span><ins>+    
+    if ($filter) {
+        push(@jscStressDriverCmd, &quot;--filter&quot;);
+        push(@jscStressDriverCmd, $filter);
+    }
</ins><span class="cx"> 
</span><span class="cx">     # End option processing, the rest of the arguments are tests
</span><span class="cx">     push(@jscStressDriverCmd, &quot;--&quot;);
</span></span></pre></div>
<a id="trunkToolsScriptsrunjscstresstests"></a>
<div class="modfile"><h4>Modified: trunk/Tools/Scripts/run-jsc-stress-tests (183973 => 183974)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/Scripts/run-jsc-stress-tests        2015-05-08 02:05:15 UTC (rev 183973)
+++ trunk/Tools/Scripts/run-jsc-stress-tests        2015-05-08 02:12:35 UTC (rev 183974)
</span><span class="lines">@@ -105,6 +105,7 @@
</span><span class="cx"> $remoteDirectory = nil
</span><span class="cx"> $architecture = nil
</span><span class="cx"> $hostOS = nil
</span><ins>+$filter = nil
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx"> def usage
</span><span class="lines">@@ -130,6 +131,7 @@
</span><span class="cx">     puts &quot;--remote                    Specify a remote host on which to run tests from command line argument.&quot;
</span><span class="cx">     puts &quot;--remote-config-file        Specify a remote host on which to run tests from JSON file.&quot;
</span><span class="cx">     puts &quot;--child-processes    (-c)   Specify the number of child processes.&quot;
</span><ins>+    puts &quot;--filter                    Only run tests whose name matches the given regular expression.&quot;
</ins><span class="cx">     puts &quot;--help               (-h)   Print this message.&quot;
</span><span class="cx">     exit 1
</span><span class="cx"> end
</span><span class="lines">@@ -152,6 +154,7 @@
</span><span class="cx">                ['--remote', GetoptLong::REQUIRED_ARGUMENT],
</span><span class="cx">                ['--remote-config-file', GetoptLong::REQUIRED_ARGUMENT],
</span><span class="cx">                ['--child-processes', '-c', GetoptLong::REQUIRED_ARGUMENT],
</span><ins>+               ['--filter', GetoptLong::REQUIRED_ARGUMENT],
</ins><span class="cx">                ['--verbose', '-v', GetoptLong::NO_ARGUMENT]).each {
</span><span class="cx">     | opt, arg |
</span><span class="cx">     case opt
</span><span class="lines">@@ -191,6 +194,8 @@
</span><span class="cx">         $remoteConfigFile = arg
</span><span class="cx">     when '--child-processes'
</span><span class="cx">         $numChildProcesses = arg.to_i
</span><ins>+    when '--filter'
+        $filter = Regexp.new(arg)
</ins><span class="cx">     when '--arch'
</span><span class="cx">         $architecture = arg
</span><span class="cx">     when '--os'
</span><span class="lines">@@ -594,7 +599,11 @@
</span><span class="cx"> 
</span><span class="cx"> def addRunCommand(kind, command, outputHandler, errorHandler)
</span><span class="cx">     $didAddRunCommand = true
</span><del>-    plan = Plan.new($benchmarkDirectory, command, baseOutputName(kind), outputHandler, errorHandler)
</del><ins>+    name = baseOutputName(kind)
+    if $filter and name !~ $filter
+        return
+    end
+    plan = Plan.new($benchmarkDirectory, command, name, outputHandler, errorHandler)
</ins><span class="cx">     if $numChildProcesses &gt; 1 and $runCommandOptions[:isSlow]
</span><span class="cx">         $runlist.unshift plan
</span><span class="cx">     else
</span><span class="lines">@@ -644,6 +653,10 @@
</span><span class="cx">     run(&quot;default&quot;)
</span><span class="cx"> end
</span><span class="cx"> 
</span><ins>+def runWithRAMSize(size)
+    run(&quot;ram-size-#{size}&quot;, &quot;--forceRAMSize=#{size}&quot;)
+end
+
</ins><span class="cx"> def runNoLLInt
</span><span class="cx">     run(&quot;no-llint&quot;, &quot;--useLLInt=false&quot;)
</span><span class="cx"> end
</span></span></pre>
</div>
</div>

</body>
</html>