<!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>[213883] trunk/Source/JavaScriptCore</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/213883">213883</a></dd>
<dt>Author</dt> <dd>mark.lam@apple.com</dd>
<dt>Date</dt> <dd>2017-03-13 17:39:24 -0700 (Mon, 13 Mar 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Make the HeapVerifier useful again.
https://bugs.webkit.org/show_bug.cgi?id=161752

Reviewed by Filip Pizlo.

Resurrect the HeapVerifier.  Here's what the verifier now offers:

1. It captures the list of cells before and after GCs up to N GC cycles.
   N is set by JSC_numberOfGCCyclesToRecordForVerification.
   Currently, N defaults to 3.

   This is useful if we're debugging in lldb and want to check if a candidate
   cell pointer was observed by the GC during the last N GC cycles.  We can do
   this check buy calling HeapVerifier::checkIfRecorded() with the cell address.

   HeapVerifier::checkIfRecorded() is robust and can be used on bogus addresses.
   If the candidate cell was previously recorded by the HeapVerifier during a
   GC cycle, checkIfRecorded() will dump any useful info it has on that cell.

2. The HeapVerifier will verify that cells in its captured list after a GC are
   sane.  Some examples of cell insanity are:
   - the cell claims to belong to a different VM.
   - the cell has a NULL structureID.
   - the cell has a NULL structure.
   - the cell's structure has a NULL structureID.
   - the cell's structure has a NULL structure.
   - the cell's structure's structure has a NULL structureID.
   - the cell's structure's structure has a NULL structure.

   These are all signs of corruption or a GC bug.  The verifier will report any
   insanity it finds, and then crash with a RELEASE_ASSERT.

3. Since the HeapVerifier captures list of cells in the heap before and after GCs
   for the last N GCs, it will also automatically &quot;trim&quot; dead cells those list
   after the most recent GC.

   &quot;trim&quot; here means that the CellProfile in the HeapVerifier's lists will be
   updated to reflect that the cell is now dead.  It still keeps a record of the
   dead cell pointer and the meta data collected about it back when it was alive.
   As a result, checkIfRecorded() will also report if the candidate cell passed
   to it is a dead object from a previous GC cycle. 

4. Each CellProfile captured by the HeapVerifier now track the following info:
   - the cell's HeapCell::Kind.
   - the cell's liveness.
   - if is JSCell, the cell's classInfo()-&gt;className.
   - an associated timestamp.
   - an associated stack trace.

   Currently, the timestamp is only used for the time when the cell was recorded
   by the HeapVerifier during GC.  The stack trace is currently unused.

   However, these fields are kept there so that we can instrument the VM (during
   a debugging session, which requires rebuilding the VM) and record interesting
   stack traces like that of the time of allocation of the cell.  Since
   capturing the stack traces for each cell is a very heavy weight operation,
   the HeapVerifier code does not do this by default.  Instead, we just leave
   the building blocks for doing so in place to ease future debugging efforts.

* heap/Heap.cpp:
(JSC::Heap::runBeginPhase):
(JSC::Heap::runEndPhase):
(JSC::Heap::didFinishCollection):
* heap/Heap.h:
(JSC::Heap::verifier):
* heap/MarkedAllocator.h:
(JSC::MarkedAllocator::takeLastActiveBlock): Deleted.
* heap/MarkedSpace.h:
* heap/MarkedSpaceInlines.h:
(JSC::MarkedSpace::forEachLiveCell):
* tools/CellList.cpp:
(JSC::CellList::find):
(JSC::CellList::reset):
(JSC::CellList::findCell): Deleted.
* tools/CellList.h:
(JSC::CellList::CellList):
(JSC::CellList::name):
(JSC::CellList::size):
(JSC::CellList::cells):
(JSC::CellList::add):
(JSC::CellList::reset): Deleted.
* tools/CellProfile.h:
(JSC::CellProfile::CellProfile):
(JSC::CellProfile::cell):
(JSC::CellProfile::jsCell):
(JSC::CellProfile::isJSCell):
(JSC::CellProfile::kind):
(JSC::CellProfile::isLive):
(JSC::CellProfile::isDead):
(JSC::CellProfile::setIsLive):
(JSC::CellProfile::setIsDead):
(JSC::CellProfile::timestamp):
(JSC::CellProfile::className):
(JSC::CellProfile::stackTrace):
(JSC::CellProfile::setStackTrace):
* tools/HeapVerifier.cpp:
(JSC::HeapVerifier::startGC):
(JSC::HeapVerifier::endGC):
(JSC::HeapVerifier::gatherLiveCells):
(JSC::trimDeadCellsFromList):
(JSC::HeapVerifier::trimDeadCells):
(JSC::HeapVerifier::printVerificationHeader):
(JSC::HeapVerifier::verifyCellList):
(JSC::HeapVerifier::validateCell):
(JSC::HeapVerifier::validateJSCell):
(JSC::HeapVerifier::verify):
(JSC::HeapVerifier::reportCell):
(JSC::HeapVerifier::checkIfRecorded):
(JSC::HeapVerifier::initializeGCCycle): Deleted.
(JSC::GatherCellFunctor::GatherCellFunctor): Deleted.
(JSC::GatherCellFunctor::visit): Deleted.
(JSC::GatherCellFunctor::operator()): Deleted.
(JSC::HeapVerifier::verifyButterflyIsInStorageSpace): Deleted.
* tools/HeapVerifier.h:
(JSC::HeapVerifier::GCCycle::reset):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapcpp">trunk/Source/JavaScriptCore/heap/Heap.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeaph">trunk/Source/JavaScriptCore/heap/Heap.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapMarkedAllocatorh">trunk/Source/JavaScriptCore/heap/MarkedAllocator.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapMarkedSpaceh">trunk/Source/JavaScriptCore/heap/MarkedSpace.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapMarkedSpaceInlinesh">trunk/Source/JavaScriptCore/heap/MarkedSpaceInlines.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretoolsCellListcpp">trunk/Source/JavaScriptCore/tools/CellList.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoretoolsCellListh">trunk/Source/JavaScriptCore/tools/CellList.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretoolsCellProfileh">trunk/Source/JavaScriptCore/tools/CellProfile.h</a></li>
<li><a href="#trunkSourceJavaScriptCoretoolsHeapVerifiercpp">trunk/Source/JavaScriptCore/tools/HeapVerifier.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoretoolsHeapVerifierh">trunk/Source/JavaScriptCore/tools/HeapVerifier.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/ChangeLog        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -1,3 +1,121 @@
</span><ins>+2017-03-13  Mark Lam  &lt;mark.lam@apple.com&gt;
+
+        Make the HeapVerifier useful again.
+        https://bugs.webkit.org/show_bug.cgi?id=161752
+
+        Reviewed by Filip Pizlo.
+
+        Resurrect the HeapVerifier.  Here's what the verifier now offers:
+
+        1. It captures the list of cells before and after GCs up to N GC cycles.
+           N is set by JSC_numberOfGCCyclesToRecordForVerification.
+           Currently, N defaults to 3.
+
+           This is useful if we're debugging in lldb and want to check if a candidate
+           cell pointer was observed by the GC during the last N GC cycles.  We can do
+           this check buy calling HeapVerifier::checkIfRecorded() with the cell address.
+
+           HeapVerifier::checkIfRecorded() is robust and can be used on bogus addresses.
+           If the candidate cell was previously recorded by the HeapVerifier during a
+           GC cycle, checkIfRecorded() will dump any useful info it has on that cell.
+
+        2. The HeapVerifier will verify that cells in its captured list after a GC are
+           sane.  Some examples of cell insanity are:
+           - the cell claims to belong to a different VM.
+           - the cell has a NULL structureID.
+           - the cell has a NULL structure.
+           - the cell's structure has a NULL structureID.
+           - the cell's structure has a NULL structure.
+           - the cell's structure's structure has a NULL structureID.
+           - the cell's structure's structure has a NULL structure.
+
+           These are all signs of corruption or a GC bug.  The verifier will report any
+           insanity it finds, and then crash with a RELEASE_ASSERT.
+
+        3. Since the HeapVerifier captures list of cells in the heap before and after GCs
+           for the last N GCs, it will also automatically &quot;trim&quot; dead cells those list
+           after the most recent GC.
+
+           &quot;trim&quot; here means that the CellProfile in the HeapVerifier's lists will be
+           updated to reflect that the cell is now dead.  It still keeps a record of the
+           dead cell pointer and the meta data collected about it back when it was alive.
+           As a result, checkIfRecorded() will also report if the candidate cell passed
+           to it is a dead object from a previous GC cycle. 
+
+        4. Each CellProfile captured by the HeapVerifier now track the following info:
+           - the cell's HeapCell::Kind.
+           - the cell's liveness.
+           - if is JSCell, the cell's classInfo()-&gt;className.
+           - an associated timestamp.
+           - an associated stack trace.
+
+           Currently, the timestamp is only used for the time when the cell was recorded
+           by the HeapVerifier during GC.  The stack trace is currently unused.
+
+           However, these fields are kept there so that we can instrument the VM (during
+           a debugging session, which requires rebuilding the VM) and record interesting
+           stack traces like that of the time of allocation of the cell.  Since
+           capturing the stack traces for each cell is a very heavy weight operation,
+           the HeapVerifier code does not do this by default.  Instead, we just leave
+           the building blocks for doing so in place to ease future debugging efforts.
+
+        * heap/Heap.cpp:
+        (JSC::Heap::runBeginPhase):
+        (JSC::Heap::runEndPhase):
+        (JSC::Heap::didFinishCollection):
+        * heap/Heap.h:
+        (JSC::Heap::verifier):
+        * heap/MarkedAllocator.h:
+        (JSC::MarkedAllocator::takeLastActiveBlock): Deleted.
+        * heap/MarkedSpace.h:
+        * heap/MarkedSpaceInlines.h:
+        (JSC::MarkedSpace::forEachLiveCell):
+        * tools/CellList.cpp:
+        (JSC::CellList::find):
+        (JSC::CellList::reset):
+        (JSC::CellList::findCell): Deleted.
+        * tools/CellList.h:
+        (JSC::CellList::CellList):
+        (JSC::CellList::name):
+        (JSC::CellList::size):
+        (JSC::CellList::cells):
+        (JSC::CellList::add):
+        (JSC::CellList::reset): Deleted.
+        * tools/CellProfile.h:
+        (JSC::CellProfile::CellProfile):
+        (JSC::CellProfile::cell):
+        (JSC::CellProfile::jsCell):
+        (JSC::CellProfile::isJSCell):
+        (JSC::CellProfile::kind):
+        (JSC::CellProfile::isLive):
+        (JSC::CellProfile::isDead):
+        (JSC::CellProfile::setIsLive):
+        (JSC::CellProfile::setIsDead):
+        (JSC::CellProfile::timestamp):
+        (JSC::CellProfile::className):
+        (JSC::CellProfile::stackTrace):
+        (JSC::CellProfile::setStackTrace):
+        * tools/HeapVerifier.cpp:
+        (JSC::HeapVerifier::startGC):
+        (JSC::HeapVerifier::endGC):
+        (JSC::HeapVerifier::gatherLiveCells):
+        (JSC::trimDeadCellsFromList):
+        (JSC::HeapVerifier::trimDeadCells):
+        (JSC::HeapVerifier::printVerificationHeader):
+        (JSC::HeapVerifier::verifyCellList):
+        (JSC::HeapVerifier::validateCell):
+        (JSC::HeapVerifier::validateJSCell):
+        (JSC::HeapVerifier::verify):
+        (JSC::HeapVerifier::reportCell):
+        (JSC::HeapVerifier::checkIfRecorded):
+        (JSC::HeapVerifier::initializeGCCycle): Deleted.
+        (JSC::GatherCellFunctor::GatherCellFunctor): Deleted.
+        (JSC::GatherCellFunctor::visit): Deleted.
+        (JSC::GatherCellFunctor::operator()): Deleted.
+        (JSC::HeapVerifier::verifyButterflyIsInStorageSpace): Deleted.
+        * tools/HeapVerifier.h:
+        (JSC::HeapVerifier::GCCycle::reset):
+
</ins><span class="cx"> 2017-03-13  SKumarMetro  &lt;s.kumar@metrological.com&gt;
</span><span class="cx"> 
</span><span class="cx">         JSC: fix compilation errors for MIPS
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/Heap.cpp (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/Heap.cpp        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/heap/Heap.cpp        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -1097,12 +1097,12 @@
</span><span class="cx">         
</span><span class="cx">     willStartCollection(scope);
</span><span class="cx">         
</span><del>-    if (m_verifier) {
</del><ins>+    if (UNLIKELY(m_verifier)) {
</ins><span class="cx">         // Verify that live objects from the last GC cycle haven't been corrupted by
</span><span class="cx">         // mutators before we begin this new GC cycle.
</span><span class="cx">         m_verifier-&gt;verify(HeapVerifier::Phase::BeforeGC);
</span><span class="cx">             
</span><del>-        m_verifier-&gt;initializeGCCycle();
</del><ins>+        m_verifier-&gt;startGC();
</ins><span class="cx">         m_verifier-&gt;gatherLiveCells(HeapVerifier::Phase::BeforeMarking);
</span><span class="cx">     }
</span><span class="cx">         
</span><span class="lines">@@ -1332,7 +1332,7 @@
</span><span class="cx">     updateObjectCounts();
</span><span class="cx">     endMarking();
</span><span class="cx">         
</span><del>-    if (m_verifier) {
</del><ins>+    if (UNLIKELY(m_verifier)) {
</ins><span class="cx">         m_verifier-&gt;gatherLiveCells(HeapVerifier::Phase::AfterMarking);
</span><span class="cx">         m_verifier-&gt;verify(HeapVerifier::Phase::AfterMarking);
</span><span class="cx">     }
</span><span class="lines">@@ -1357,13 +1357,13 @@
</span><span class="cx">     m_objectSpace.prepareForAllocation();
</span><span class="cx">     updateAllocationLimits();
</span><span class="cx"> 
</span><del>-    didFinishCollection();
-    
-    if (m_verifier) {
</del><ins>+    if (UNLIKELY(m_verifier)) {
</ins><span class="cx">         m_verifier-&gt;trimDeadCells();
</span><span class="cx">         m_verifier-&gt;verify(HeapVerifier::Phase::AfterGC);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    didFinishCollection();
+
</ins><span class="cx">     if (false) {
</span><span class="cx">         dataLog(&quot;Heap state after GC:\n&quot;);
</span><span class="cx">         m_objectSpace.dumpBits();
</span><span class="lines">@@ -2162,6 +2162,9 @@
</span><span class="cx">         removeDeadHeapSnapshotNodes(*heapProfiler);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    if (UNLIKELY(m_verifier))
+        m_verifier-&gt;endGC();
+
</ins><span class="cx">     RELEASE_ASSERT(m_collectionScope);
</span><span class="cx">     m_lastCollectionScope = m_collectionScope;
</span><span class="cx">     m_collectionScope = std::nullopt;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeaph"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/Heap.h (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/Heap.h        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/heap/Heap.h        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -351,7 +351,9 @@
</span><span class="cx">     CFRunLoopRef runLoop() const { return m_runLoop.get(); }
</span><span class="cx">     JS_EXPORT_PRIVATE void setRunLoop(CFRunLoopRef);
</span><span class="cx"> #endif // USE(CF)
</span><del>-    
</del><ins>+
+    HeapVerifier* verifier() const { return m_verifier.get(); }
+
</ins><span class="cx"> private:
</span><span class="cx">     friend class AllocatingScope;
</span><span class="cx">     friend class CodeBlock;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapMarkedAllocatorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/MarkedAllocator.h (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/MarkedAllocator.h        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/heap/MarkedAllocator.h        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -42,7 +42,7 @@
</span><span class="cx"> #define FOR_EACH_MARKED_ALLOCATOR_BIT(macro) \
</span><span class="cx">     macro(live, Live) /* The set of block indices that have actual blocks. */\
</span><span class="cx">     macro(empty, Empty) /* The set of all blocks that have no live objects and nothing to destroy. */ \
</span><del>-    macro(allocated, Allocated) /* The set of allblocks that are full of live objects. */\
</del><ins>+    macro(allocated, Allocated) /* The set of all blocks that are full of live objects. */\
</ins><span class="cx">     macro(canAllocateButNotEmpty, CanAllocateButNotEmpty) /* The set of all blocks are neither empty nor retired (i.e. are more than minMarkedBlockUtilization full). */ \
</span><span class="cx">     macro(eden, Eden) /* The set of all blocks that have new objects since the last GC. */\
</span><span class="cx">     macro(unswept, Unswept) /* The set of all blocks that could be swept by the incremental sweeper. */\
</span><span class="lines">@@ -155,13 +155,7 @@
</span><span class="cx">     void* allocate(GCDeferralContext* = nullptr);
</span><span class="cx">     void* tryAllocate(GCDeferralContext* = nullptr);
</span><span class="cx">     Heap* heap() { return m_heap; }
</span><del>-    MarkedBlock::Handle* takeLastActiveBlock()
-    {
-        MarkedBlock::Handle* block = m_lastActiveBlock;
-        m_lastActiveBlock = 0;
-        return block;
-    }
-    
</del><ins>+
</ins><span class="cx">     template&lt;typename Functor&gt; void forEachBlock(const Functor&amp;);
</span><span class="cx">     template&lt;typename Functor&gt; void forEachNotEmptyBlock(const Functor&amp;);
</span><span class="cx">     
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapMarkedSpaceh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/MarkedSpace.h (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/MarkedSpace.h        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/heap/MarkedSpace.h        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -178,6 +178,10 @@
</span><span class="cx">     void* allocateSlow(Subspace&amp;, GCDeferralContext*, size_t);
</span><span class="cx">     void* tryAllocateSlow(Subspace&amp;, GCDeferralContext*, size_t);
</span><span class="cx"> 
</span><ins>+    // Use this version when calling from within the GC where we know that the allocators
+    // have already been stopped.
+    template&lt;typename Functor&gt; void forEachLiveCell(const Functor&amp;);
+
</ins><span class="cx">     static void initializeSizeClassForStepSize();
</span><span class="cx">     
</span><span class="cx">     void initializeSubspace(Subspace&amp;);
</span><span class="lines">@@ -212,6 +216,8 @@
</span><span class="cx">     MarkedAllocator* m_firstAllocator { nullptr };
</span><span class="cx">     MarkedAllocator* m_lastAllocator { nullptr };
</span><span class="cx">     MarkedAllocator* m_allocatorForEmptyAllocation { nullptr };
</span><ins>+
+    friend class HeapVerifier;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> template &lt;typename Functor&gt; inline void MarkedSpace::forEachBlock(const Functor&amp; functor)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapMarkedSpaceInlinesh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/MarkedSpaceInlines.h (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/MarkedSpaceInlines.h        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/heap/MarkedSpaceInlines.h        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2016 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -33,6 +33,11 @@
</span><span class="cx"> template&lt;typename Functor&gt; inline void MarkedSpace::forEachLiveCell(HeapIterationScope&amp;, const Functor&amp; functor)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(isIterating());
</span><ins>+    forEachLiveCell(functor);
+}
+
+template&lt;typename Functor&gt; inline void MarkedSpace::forEachLiveCell(const Functor&amp; functor)
+{
</ins><span class="cx">     BlockIterator end = m_blocks.set().end();
</span><span class="cx">     for (BlockIterator it = m_blocks.set().begin(); it != end; ++it) {
</span><span class="cx">         if ((*it)-&gt;handle().forEachLiveCell(functor) == IterationStatus::Done)
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretoolsCellListcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tools/CellList.cpp (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tools/CellList.cpp        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/tools/CellList.cpp        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -28,13 +28,25 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><del>-CellProfile* CellList::findCell(JSCell* cell)
</del><ins>+CellProfile* CellList::find(HeapCell* cell)
</ins><span class="cx"> {
</span><del>-    for (auto&amp; profile : liveCells) {
-        if (cell == profile.cell)
-            return &amp;profile;
</del><ins>+    if (!size())
+        return nullptr;
+
+    if (!m_mapIsUpToDate) {
+        m_map.clear();
+        for (auto&amp; profile : m_cells)
+            m_map.add(profile.cell(), &amp;profile);
+        m_mapIsUpToDate = true;
</ins><span class="cx">     }
</span><del>-    return nullptr;
</del><ins>+    return m_map.get(cell);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void CellList::reset()
+{
+    m_cells.clear();
+    m_map.clear();
+    m_mapIsUpToDate = false;
+}
+
</ins><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretoolsCellListh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tools/CellList.h (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tools/CellList.h        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/tools/CellList.h        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -26,28 +26,41 @@
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><span class="cx"> #include &quot;CellProfile.h&quot;
</span><del>-#include &lt;wtf/Vector.h&gt;
</del><ins>+#include &lt;wtf/HashMap.h&gt;
+#include &lt;wtf/SegmentedVector.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><del>-struct CellList {
</del><ins>+class CellList {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
</ins><span class="cx">     CellList(const char* name)
</span><del>-        : name(name)
-        , hasLiveCells(true)
</del><ins>+        : m_name(name)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    void reset()
</del><ins>+    const char* name() const { return m_name; }
+    size_t size() const { return m_cells.size(); }
+
+    typedef SegmentedVector&lt;CellProfile, 64&gt; CellProfileVector;
+    CellProfileVector&amp; cells() { return m_cells; }
+
+    void add(CellProfile&amp;&amp; profile)
</ins><span class="cx">     {
</span><del>-        liveCells.clear();
-        hasLiveCells = true; // Presume to have live objects until the list is trimmed.
</del><ins>+        m_cells.append(WTFMove(profile));
+        m_mapIsUpToDate = false;
</ins><span class="cx">     }
</span><del>-    
-    CellProfile* findCell(JSCell*);
-    
-    const char* name;
-    Vector&lt;CellProfile&gt; liveCells;
-    bool hasLiveCells;
</del><ins>+
+    void reset();
+
+    CellProfile* find(HeapCell*);
+
+private:
+    const char* m_name;
+    CellProfileVector m_cells;
+
+    bool m_mapIsUpToDate { false };
+    HashMap&lt;HeapCell*, CellProfile*&gt; m_map;
</ins><span class="cx"> };
</span><span class="cx">     
</span><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretoolsCellProfileh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tools/CellProfile.h (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tools/CellProfile.h        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/tools/CellProfile.h        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -25,19 +25,70 @@
</span><span class="cx"> 
</span><span class="cx"> #pragma once
</span><span class="cx"> 
</span><ins>+#include &quot;JSCell.h&quot;
+#include &quot;StackTrace.h&quot;
+#include &quot;Structure.h&quot;
+#include &lt;wtf/MonotonicTime.h&gt;
+
</ins><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><del>-class JSCell;
</del><ins>+struct CellProfile {
+    enum Liveness {
+        Unknown,
+        Dead,
+        Live
+    };
</ins><span class="cx"> 
</span><del>-struct CellProfile {
-    CellProfile(JSCell* cell, bool isConfirmedDead = false)
-        : cell(cell)
-        , isConfirmedDead(isConfirmedDead)
</del><ins>+    CellProfile(HeapCell* cell, HeapCell::Kind kind, Liveness liveness)
+        : m_cell(cell)
+        , m_kind(kind)
+        , m_liveness(liveness)
+        , m_timestamp(MonotonicTime::now())
</ins><span class="cx">     {
</span><ins>+        if (m_kind == HeapCell::JSCell &amp;&amp; m_liveness != Dead)
+            m_className = jsCell()-&gt;structure()-&gt;classInfo()-&gt;className;
</ins><span class="cx">     }
</span><ins>+
+    CellProfile(CellProfile&amp;&amp; other)
+        : m_cell(other.m_cell)
+        , m_kind(other.m_kind)
+        , m_liveness(other.m_liveness)
+        , m_timestamp(other.m_timestamp)
+        , m_className(other.m_className)
+        , m_stackTrace(WTFMove(other.m_stackTrace))
+    { }
+
+    HeapCell* cell() const { return m_cell; }
+    JSCell* jsCell() const
+    {
+        ASSERT(isJSCell());
+        return static_cast&lt;JSCell*&gt;(m_cell);
+    }
+
+    bool isJSCell() const { return m_kind == HeapCell::JSCell; }
</ins><span class="cx">     
</span><del>-    JSCell* cell;
-    bool isConfirmedDead;
</del><ins>+    HeapCell::Kind kind() const { return m_kind; }
+
+    bool isLive() const { return m_liveness == Live; }
+    bool isDead() const { return m_liveness == Dead; }
+
+    void setIsLive() { m_liveness = Live; }
+    void setIsDead() { m_liveness = Dead; }
+
+    MonotonicTime timestamp() const { return m_timestamp; }
+
+    const char* className() const { return m_className; }
+
+    StackTrace* stackTrace() const { return m_stackTrace.get(); }
+    void setStackTrace(StackTrace* trace) { m_stackTrace = std::unique_ptr&lt;StackTrace&gt;(trace); }
+
+private:
+    HeapCell* m_cell;
+    HeapCell::Kind m_kind;
+    Liveness m_liveness { Unknown };
+    MonotonicTime m_timestamp;
+    const char* m_className { nullptr };
+    std::unique_ptr&lt;StackTrace&gt; m_stackTrace;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretoolsHeapVerifiercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tools/HeapVerifier.cpp (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tools/HeapVerifier.cpp        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/tools/HeapVerifier.cpp        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2014, 2016 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2014-2017 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -26,11 +26,14 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;HeapVerifier.h&quot;
</span><span class="cx"> 
</span><del>-#include &quot;ButterflyInlines.h&quot;
</del><ins>+#include &quot;CodeBlock.h&quot;
</ins><span class="cx"> #include &quot;HeapIterationScope.h&quot;
</span><span class="cx"> #include &quot;JSCInlines.h&quot;
</span><span class="cx"> #include &quot;JSObject.h&quot;
</span><span class="cx"> #include &quot;MarkedSpaceInlines.h&quot;
</span><ins>+#include &quot;VMInspector.h&quot;
+#include &quot;ValueProfile.h&quot;
+#include &lt;wtf/ProcessID.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><span class="lines">@@ -59,48 +62,36 @@
</span><span class="cx">     return nullptr; // Silencing a compiler warning.
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HeapVerifier::initializeGCCycle()
</del><ins>+void HeapVerifier::startGC()
</ins><span class="cx"> {
</span><span class="cx">     Heap* heap = m_heap;
</span><span class="cx">     incrementCycle();
</span><ins>+    currentCycle().reset();
</ins><span class="cx">     currentCycle().scope = *heap-&gt;collectionScope();
</span><ins>+    currentCycle().timestamp = MonotonicTime::now();
+    ASSERT(!m_didPrintLogs);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-struct GatherCellFunctor : MarkedBlock::CountFunctor {
-    GatherCellFunctor(CellList&amp; list)
-        : m_list(list)
-    {
-        ASSERT(!list.liveCells.size());
</del><ins>+void HeapVerifier::endGC()
+{
+    if (m_didPrintLogs) {
+        dataLog(&quot;END &quot;);
+        printVerificationHeader();
+        dataLog(&quot;\n\n&quot;);
+        m_didPrintLogs = false;
</ins><span class="cx">     }
</span><ins>+}
</ins><span class="cx"> 
</span><del>-    inline void visit(JSCell* cell)
-    {
-        CellProfile profile(cell);
-        m_list.liveCells.append(profile);
-    }
-
-    IterationStatus operator()(HeapCell* cell, HeapCell::Kind kind) const
-    {
-        if (kind == HeapCell::JSCell) {
-            // FIXME: This const_cast exists because this isn't a C++ lambda.
-            // https://bugs.webkit.org/show_bug.cgi?id=159644
-            const_cast&lt;GatherCellFunctor*&gt;(this)-&gt;visit(static_cast&lt;JSCell*&gt;(cell));
-        }
-        return IterationStatus::Continue;
-    }
-
-    CellList&amp; m_list;
-};
-
</del><span class="cx"> void HeapVerifier::gatherLiveCells(HeapVerifier::Phase phase)
</span><span class="cx"> {
</span><span class="cx">     Heap* heap = m_heap;
</span><span class="cx">     CellList&amp; list = *cellListForGathering(phase);
</span><span class="cx"> 
</span><del>-    HeapIterationScope iterationScope(*heap);
</del><span class="cx">     list.reset();
</span><del>-    GatherCellFunctor functor(list);
-    heap-&gt;m_objectSpace.forEachLiveCell(iterationScope, functor);
</del><ins>+    heap-&gt;m_objectSpace.forEachLiveCell([&amp;list] (HeapCell* cell, HeapCell::Kind kind) {
+        list.add({ cell, kind, CellProfile::Live });
+        return IterationStatus::Continue;
+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> CellList* HeapVerifier::cellListForGathering(HeapVerifier::Phase phase)
</span><span class="lines">@@ -119,32 +110,26 @@
</span><span class="cx">     return nullptr; // Silencing a compiler warning.
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void trimDeadCellsFromList(HashSet&lt;JSCell*&gt;&amp; knownLiveSet, CellList&amp; list)
</del><ins>+static void trimDeadCellsFromList(CellList&amp; knownLiveSet, CellList&amp; list)
</ins><span class="cx"> {
</span><del>-    if (!list.hasLiveCells)
</del><ins>+    if (!list.size())
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    size_t liveCellsFound = 0;
-    for (auto&amp; cellProfile : list.liveCells) {
-        if (cellProfile.isConfirmedDead)
</del><ins>+    for (auto&amp; cellProfile : list.cells()) {
+        if (cellProfile.isDead())
</ins><span class="cx">             continue; // Don't &quot;resurrect&quot; known dead cells.
</span><del>-        if (!knownLiveSet.contains(cellProfile.cell)) {
-            cellProfile.isConfirmedDead = true;
</del><ins>+        if (!knownLiveSet.find(cellProfile.cell())) {
+            cellProfile.setIsDead();
</ins><span class="cx">             continue;
</span><span class="cx">         }
</span><del>-        liveCellsFound++;
</del><ins>+        cellProfile.setIsLive();
</ins><span class="cx">     }
</span><del>-    list.hasLiveCells = !!liveCellsFound;
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HeapVerifier::trimDeadCells()
</span><span class="cx"> {
</span><del>-    HashSet&lt;JSCell*&gt; knownLiveSet;
</del><ins>+    CellList&amp; knownLiveSet = currentCycle().after;
</ins><span class="cx"> 
</span><del>-    CellList&amp; after = currentCycle().after;
-    for (auto&amp; cellProfile : after.liveCells)
-        knownLiveSet.add(cellProfile.cell);
-
</del><span class="cx">     trimDeadCellsFromList(knownLiveSet, currentCycle().before);
</span><span class="cx"> 
</span><span class="cx">     for (int i = -1; i &gt; -m_numberOfCycles; i--) {
</span><span class="lines">@@ -153,71 +138,330 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool HeapVerifier::verifyButterflyIsInStorageSpace(Phase, CellList&amp;)
</del><ins>+void HeapVerifier::printVerificationHeader()
</ins><span class="cx"> {
</span><del>-    // FIXME: Make this work again. https://bugs.webkit.org/show_bug.cgi?id=161752
</del><ins>+    RELEASE_ASSERT(m_heap-&gt;collectionScope());
+    CollectionScope scope = currentCycle().scope;
+    MonotonicTime gcCycleTimestamp = currentCycle().timestamp;
+    dataLog(&quot;Verifying heap in [p&quot;, getCurrentProcessID(), &quot;, t&quot;, currentThread(), &quot;] vm &quot;,
+        RawPointer(m_heap-&gt;vm()), &quot; on &quot;, scope, &quot; GC @ &quot;, gcCycleTimestamp, &quot;\n&quot;);
+}
+
+bool HeapVerifier::verifyCellList(Phase phase, CellList&amp; list)
+{
+    VM&amp; vm = *m_heap-&gt;vm();
+    auto&amp; liveCells = list.cells();
+
+    bool listNamePrinted = false;
+    auto printHeaderIfNeeded = [&amp;] () {
+        if (listNamePrinted)
+            return;
+        
+        printVerificationHeader();
+        dataLog(&quot; @ phase &quot;, phaseName(phase), &quot;: FAILED in cell list '&quot;, list.name(), &quot;' (size &quot;, liveCells.size(), &quot;)\n&quot;);
+        listNamePrinted = true;
+        m_didPrintLogs = true;
+    };
+    
+    bool success = true;
+    for (size_t i = 0; i &lt; liveCells.size(); i++) {
+        CellProfile&amp; profile = liveCells[i];
+        if (!profile.isLive())
+            continue;
+
+        if (!profile.isJSCell())
+            continue;
+
+        JSCell* cell = profile.jsCell();
+        success |= validateJSCell(&amp;vm, cell, &amp;profile, &amp;list, printHeaderIfNeeded, &quot;  &quot;);
+    }
+
+    return success;
+}
+
+bool HeapVerifier::validateCell(HeapCell* cell, VM* expectedVM)
+{
+    auto printNothing = [] () { };
+
+    if (cell-&gt;isZapped()) {
+        dataLog(&quot;    cell &quot;, RawPointer(cell), &quot; is ZAPPED\n&quot;);
+        return false;
+    }
+
+    if (cell-&gt;cellKind() != HeapCell::JSCell)
+        return true; // Nothing more to validate.
+
+    JSCell* jsCell = static_cast&lt;JSCell*&gt;(cell);
+    return validateJSCell(expectedVM, jsCell, nullptr, nullptr, printNothing);
+}
+
+bool HeapVerifier::validateJSCell(VM* expectedVM, JSCell* cell, CellProfile* profile, CellList* list, std::function&lt;void()&gt; printHeaderIfNeeded, const char* prefix)
+{
+    auto printHeaderAndCell = [cell, profile, printHeaderIfNeeded, prefix] () {
+        printHeaderIfNeeded();
+        dataLog(prefix, &quot;cell &quot;, RawPointer(cell));
+        if (profile)
+            dataLog(&quot; [&quot;, profile-&gt;className(), &quot;]&quot;);
+    };
+
+    // 1. Validate the cell.
+
+    if (cell-&gt;isZapped()) {
+        printHeaderAndCell();
+        dataLog(&quot; is zapped\n&quot;);
+        return false;
+    }
+
+    StructureID structureID = cell-&gt;structureID();
+    if (!structureID) {
+        printHeaderAndCell();
+        dataLog(&quot; has NULL structureID\n&quot;);
+        return false;
+    }
+
+    if (expectedVM) {
+        VM&amp; vm = *expectedVM;
+
+        VM* cellVM = cell-&gt;vm();
+        if (cellVM != expectedVM) {
+            printHeaderAndCell();
+            dataLog(&quot; is from a different VM: expected:&quot;, RawPointer(expectedVM), &quot; actual:&quot;, RawPointer(cellVM), &quot;\n&quot;);
+            return false;
+        }
+
+        // 2. Validate the cell's structure
+
+        Structure* structure = vm.getStructure(structureID);
+        if (!structure) {
+            printHeaderAndCell();
+#if USE(JSVALUE64)
+            uint32_t structureIDAsUint32 = structureID;
+#else
+            uint32_t structureIDAsUint32 = reinterpret_cast&lt;uint32_t&gt;(structureID);
+#endif
+            dataLog(&quot; with structureID &quot;, structureIDAsUint32, &quot; maps to a NULL Structure pointer\n&quot;);
+            return false;
+        }
+
+        if (structure-&gt;isZapped()) {
+            printHeaderAndCell();
+            dataLog(&quot; has ZAPPED structure &quot;, RawPointer(structure), &quot;\n&quot;);
+            return false;
+        }
+
+        if (!structure-&gt;structureID()) {
+            printHeaderAndCell();
+            dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; whose structureID is NULL\n&quot;);
+            return false;
+        }
+
+        VM* structureVM = structure-&gt;vm();
+        if (structureVM != expectedVM) {
+            printHeaderAndCell();
+            dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; from a different VM: expected:&quot;, RawPointer(expectedVM), &quot; actual:&quot;, RawPointer(structureVM), &quot;\n&quot;);
+            return false;
+        }
+
+        if (list) {
+            auto* structureProfile = list-&gt;find(structure);
+            if (!structureProfile) {
+                printHeaderAndCell();
+                dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; NOT found in the live cell list\n&quot;);
+                return false;
+            }
+
+            if (!structureProfile-&gt;isLive()) {
+                printHeaderAndCell();
+                dataLog(&quot; has DEAD structure &quot;, RawPointer(structure), &quot;\n&quot;);
+                return false;
+            }
+        }
+
+        StructureID structureStructureID = structure-&gt;structureID();
+        if (!structureStructureID) {
+            printHeaderAndCell();
+            dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; with a NULL structureID\n&quot;);
+            return false;
+        }
+
+        // 3. Validate the cell's structure's structure.
+        
+        Structure* structureStructure = vm.getStructure(structureID);
+        if (!structureStructure) {
+            printHeaderAndCell();
+            dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; whose structure is NULL\n&quot;);
+            return false;
+        }
+        
+        if (structureStructure-&gt;isZapped()) {
+            printHeaderAndCell();
+            dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; whose structure &quot;, RawPointer(structureStructure), &quot; is ZAPPED\n&quot;);
+            return false;
+        }
+        
+        if (!structureStructure-&gt;structureID()) {
+            printHeaderAndCell();
+            dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; whose structure &quot;, RawPointer(structureStructure), &quot; has a NULL structureID\n&quot;);
+            return false;
+        }
+        
+        VM* structureStructureVM = structureStructure-&gt;vm();
+        if (structureStructureVM != expectedVM) {
+            printHeaderAndCell();
+            dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; whose structure &quot;, RawPointer(structureStructure), &quot; is from a different VM: expected:&quot;, RawPointer(expectedVM), &quot; actual:&quot;, RawPointer(structureStructureVM), &quot;\n&quot;);
+            return false;
+        }
+        
+        if (list) {
+            auto* structureStructureProfile = list-&gt;find(structureStructure);
+            if (!structureStructureProfile) {
+                printHeaderAndCell();
+                dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; whose structure &quot;, RawPointer(structureStructure), &quot; is NOT found in the live cell list\n&quot;);
+                return false;
+            }
+            
+            if (!structureStructureProfile-&gt;isLive()) {
+                printHeaderAndCell();
+                dataLog(&quot; has structure &quot;, RawPointer(structure), &quot; whose structure &quot;, RawPointer(structureStructure), &quot; is DEAD\n&quot;);
+                return false;
+            }
+        }
+        
+        CodeBlock* codeBlock = jsDynamicCast&lt;CodeBlock*&gt;(vm, cell);
+        if (UNLIKELY(codeBlock)) {
+            bool success = true;
+            for (unsigned i = 0; i &lt; codeBlock-&gt;totalNumberOfValueProfiles(); ++i) {
+                ValueProfile* valueProfile = codeBlock-&gt;getFromAllValueProfiles(i);
+                for (unsigned i = 0; i &lt; ValueProfile::totalNumberOfBuckets; ++i) {
+                    JSValue value = JSValue::decode(valueProfile-&gt;m_buckets[i]);
+                    if (!value)
+                        continue;
+                    if (!value.isCell())
+                        continue;
+                    JSCell* valueCell = value.asCell();
+                    if (valueCell-&gt;isZapped()) {
+                        printHeaderIfNeeded();
+                        dataLog(prefix, &quot;CodeBlock &quot;, RawPointer(codeBlock), &quot; has ZAPPED ValueProfile cell &quot;, RawPointer(valueCell), &quot;\n&quot;);
+                        success = false;
+                        continue;
+                    }
+                }
+            }
+            if (!success)
+                return false;
+        }
+    }
+
</ins><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void HeapVerifier::verify(HeapVerifier::Phase phase)
</span><span class="cx"> {
</span><del>-    bool beforeVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().before);
-    bool afterVerified = verifyButterflyIsInStorageSpace(phase, currentCycle().after);
-    RELEASE_ASSERT(beforeVerified &amp;&amp; afterVerified);
</del><ins>+    if (phase == Phase::AfterGC) {
+        bool verified = verifyCellList(phase, currentCycle().after);
+        RELEASE_ASSERT(verified);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HeapVerifier::reportCell(CellProfile&amp; cellProfile, int cycleIndex, HeapVerifier::GCCycle&amp; cycle, CellList&amp; list)
</del><ins>+void HeapVerifier::reportCell(CellProfile&amp; profile, int cycleIndex, HeapVerifier::GCCycle&amp; cycle, CellList&amp; list, const char* prefix)
</ins><span class="cx"> {
</span><del>-    JSCell* cell = cellProfile.cell;
</del><ins>+    HeapCell* cell = profile.cell();
+    VM* vm = m_heap-&gt;vm();
</ins><span class="cx"> 
</span><del>-    if (cellProfile.isConfirmedDead) {
-        dataLogF(&quot;FOUND dead cell %p in GC[%d] %s list '%s'\n&quot;,
-            cell, cycleIndex, collectionScopeName(cycle.scope), list.name);
-        return;
-    }
</del><ins>+    if (prefix)
+        dataLog(prefix);
</ins><span class="cx"> 
</span><del>-    if (cell-&gt;isObject()) {
-        JSObject* object = static_cast&lt;JSObject*&gt;(cell);
-        Structure* structure = object-&gt;structure();
-        Butterfly* butterfly = object-&gt;butterfly();
-        void* butterflyBase = butterfly-&gt;base(structure);
</del><ins>+    dataLog(&quot;FOUND&quot;);
+    if (profile.isLive())
+        dataLog(&quot; LIVE&quot;);
+    else if (profile.isDead())
+        dataLog(&quot; DEAD&quot;);
</ins><span class="cx"> 
</span><del>-        dataLogF(&quot;FOUND object %p type '%s' butterfly %p (base %p) in GC[%d] %s list '%s'\n&quot;,
-            object, structure-&gt;classInfo()-&gt;className,
-            butterfly, butterflyBase,
-            cycleIndex, collectionScopeName(cycle.scope), list.name);
-    } else {
-        Structure* structure = cell-&gt;structure();
-        dataLogF(&quot;FOUND cell %p type '%s' in GC[%d] %s list '%s'\n&quot;,
-            cell, structure-&gt;classInfo()-&gt;className,
-            cycleIndex, collectionScopeName(cycle.scope), list.name);
</del><ins>+    if (!profile.isJSCell())
+        dataLog(&quot; HeapCell &quot;);
+    else
+        dataLog(&quot; JSCell &quot;);
+    dataLog(RawPointer(cell));
+
+    if (profile.className())
+        dataLog(&quot; [&quot;, profile.className(), &quot;]&quot;);
+
+    if (profile.isLive() &amp;&amp; profile.isJSCell()) {
+        JSCell* jsCell = profile.jsCell();
+        Structure* structure = jsCell-&gt;structure();
+        dataLog(&quot; structure:&quot;, RawPointer(structure));
+        if (jsCell-&gt;isObject()) {
+            JSObject* obj = static_cast&lt;JSObject*&gt;(cell);
+            Butterfly* butterfly = obj-&gt;butterfly();
+            void* butterflyBase = butterfly-&gt;base(structure);
+            
+            dataLog(&quot; butterfly:&quot;, RawPointer(butterfly), &quot; (base:&quot;, RawPointer(butterflyBase), &quot;)&quot;);
+        }
</ins><span class="cx">     }
</span><ins>+
+    dataLog(&quot; in &quot;, cycle.scope, &quot; GC[&quot;, cycleIndex, &quot;] in '&quot;, list.name(), &quot;' list in VM &quot;,
+        RawPointer(vm), &quot; recorded at time &quot;, profile.timestamp(), &quot;\n&quot;);
+    if (profile.stackTrace())
+        dataLog(*profile.stackTrace());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void HeapVerifier::checkIfRecorded(JSCell* cell)
</del><ins>+void HeapVerifier::checkIfRecorded(HeapCell* cell)
</ins><span class="cx"> {
</span><span class="cx">     bool found = false;
</span><ins>+    const char* const prefix = &quot;  &quot;;
+    static const bool verbose = true;
</ins><span class="cx"> 
</span><span class="cx">     for (int cycleIndex = 0; cycleIndex &gt; -m_numberOfCycles; cycleIndex--) {
</span><span class="cx">         GCCycle&amp; cycle = cycleForIndex(cycleIndex);
</span><del>-        CellList&amp; beforeList = cycle.before;
-        CellList&amp; afterList = cycle.after;
</del><ins>+        CellList* lists[] = { &amp;cycle.before, &amp;cycle.after };
</ins><span class="cx"> 
</span><del>-        CellProfile* profile;
-        profile = beforeList.findCell(cell);
-        if (profile) {
-            reportCell(*profile, cycleIndex, cycle, beforeList);
-            found = true;
</del><ins>+        if (verbose)
+            dataLog(&quot;Checking &quot;, cycle.scope, &quot; GC&lt;&quot;, cycle.timestamp, &quot;&gt;, cycle [&quot;, cycleIndex, &quot;]:\n&quot;);
+        
+        const char* resultPrefix = &quot;    &quot;;
+        for (auto* list : lists) {
+            if (verbose)
+                dataLog(prefix, &quot;Cycle [&quot;, cycleIndex, &quot;] '&quot;, list-&gt;name(), &quot;' list: &quot;);
+
+            CellProfile* profile = list-&gt;find(cell);
+            if (profile) {
+                reportCell(*profile, cycleIndex, cycle, *list, resultPrefix);
+                found = true;
+            } else if (verbose)
+                dataLog(resultPrefix, &quot;cell NOT found\n&quot;);
</ins><span class="cx">         }
</span><del>-        profile = afterList.findCell(cell);
-        if (profile) {
-            reportCell(*profile, cycleIndex, cycle, afterList);
-            found = true;
-        }
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (!found)
</span><del>-        dataLogF(&quot;cell %p NOT FOUND\n&quot;, cell);
</del><ins>+        dataLog(prefix, &quot;cell &quot;, RawPointer(cell), &quot; NOT FOUND\n&quot;);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+// The following are slower but more robust versions of the corresponding functions of the same name.
+// These robust versions are designed so that we can call them interactively from a C++ debugger
+// to query if a candidate is recorded cell.
+
+void HeapVerifier::checkIfRecorded(uintptr_t candidateCell)
+{
+    HeapCell* candidateHeapCell = reinterpret_cast&lt;HeapCell*&gt;(candidateCell);
+    
+    VMInspector&amp; inspector = VMInspector::instance();
+    auto expectedLocker = inspector.lock(Seconds(2));
+    if (!expectedLocker) {
+        ASSERT(expectedLocker.error() == VMInspector::Error::TimedOut);
+        dataLog(&quot;ERROR: Timed out while waiting to iterate VMs.&quot;);
+        return;
+    }
+
+    auto&amp; locker = expectedLocker.value();
+    inspector.iterate(locker, [&amp;] (VM&amp; vm) {
+        if (!vm.heap.m_verifier)
+            return VMInspector::FunctorStatus::Continue;
+        
+        auto* verifier = vm.heap.m_verifier.get();
+        dataLog(&quot;Search for cell &quot;, RawPointer(candidateHeapCell), &quot; in VM &quot;, RawPointer(&amp;vm), &quot;:\n&quot;);
+        verifier-&gt;checkIfRecorded(candidateHeapCell);
+        return VMInspector::FunctorStatus::Continue;
+    });
+}
+
</ins><span class="cx"> } // namespace JSC
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretoolsHeapVerifierh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tools/HeapVerifier.h (213882 => 213883)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tools/HeapVerifier.h        2017-03-14 00:38:18 UTC (rev 213882)
+++ trunk/Source/JavaScriptCore/tools/HeapVerifier.h        2017-03-14 00:39:24 UTC (rev 213883)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2014-2017 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -27,6 +27,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;CellList.h&quot;
</span><span class="cx"> #include &quot;Heap.h&quot;
</span><ins>+#include &lt;wtf/MonotonicTime.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC {
</span><span class="cx"> 
</span><span class="lines">@@ -45,16 +46,21 @@
</span><span class="cx"> 
</span><span class="cx">     HeapVerifier(Heap*, unsigned numberOfGCCyclesToRecord);
</span><span class="cx"> 
</span><del>-    void initializeGCCycle();
</del><ins>+    void startGC();
+    void endGC();
+
</ins><span class="cx">     void gatherLiveCells(Phase);
</span><span class="cx">     void trimDeadCells();
</span><span class="cx">     void verify(Phase);
</span><span class="cx"> 
</span><ins>+    static const char* phaseName(Phase);
+    
</ins><span class="cx">     // Scans all previously recorded CellLists and checks if the specified
</span><span class="cx">     // cell was in any of those lists.
</span><del>-    JS_EXPORT_PRIVATE void checkIfRecorded(JSCell*);
</del><ins>+    JS_EXPORT_PRIVATE static void checkIfRecorded(uintptr_t maybeCell);
</ins><span class="cx"> 
</span><del>-    static const char* phaseName(Phase);
</del><ins>+    // Returns false if anything is found to be inconsistent/incorrect about the specified cell.
+    JS_EXPORT_PRIVATE static bool validateCell(HeapCell*, VM* expectedVM = nullptr);
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     struct GCCycle {
</span><span class="lines">@@ -64,7 +70,14 @@
</span><span class="cx">         {
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        void reset()
+        {
+            before.reset();
+            after.reset();
+        }
+
</ins><span class="cx">         CollectionScope scope;
</span><ins>+        MonotonicTime timestamp;
</ins><span class="cx">         CellList before;
</span><span class="cx">         CellList after;
</span><span class="cx">     };
</span><span class="lines">@@ -82,13 +95,18 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     CellList* cellListForGathering(Phase);
</span><del>-    bool verifyButterflyIsInStorageSpace(Phase, CellList&amp;);
</del><ins>+    bool verifyCellList(Phase, CellList&amp;);
+    static bool validateJSCell(VM* expectedVM, JSCell*, CellProfile*, CellList*, std::function&lt;void()&gt; printHeaderIfNeeded, const char* prefix = &quot;&quot;);
</ins><span class="cx"> 
</span><del>-    static void reportCell(CellProfile&amp;, int cycleIndex, HeapVerifier::GCCycle&amp;, CellList&amp;);
</del><ins>+    void printVerificationHeader();
</ins><span class="cx"> 
</span><ins>+    void checkIfRecorded(HeapCell* maybeHeapCell);
+    void reportCell(CellProfile&amp;, int cycleIndex, HeapVerifier::GCCycle&amp;, CellList&amp;, const char* prefix = nullptr);
+
</ins><span class="cx">     Heap* m_heap;
</span><span class="cx">     int m_currentCycle;
</span><span class="cx">     int m_numberOfCycles;
</span><ins>+    bool m_didPrintLogs { false };
</ins><span class="cx">     std::unique_ptr&lt;GCCycle[]&gt; m_cycles;
</span><span class="cx"> };
</span><span class="cx"> 
</span></span></pre>
</div>
</div>

</body>
</html>