Title: [197489] trunk/Source/_javascript_Core
Revision
197489
Author
[email protected]
Date
2016-03-02 21:15:56 -0800 (Wed, 02 Mar 2016)

Log Message

Add ability to generate a Heap Snapshot
https://bugs.webkit.org/show_bug.cgi?id=154847

Patch by Joseph Pecoraro <[email protected]> on 2016-03-02
Reviewed by Mark Lam.

This adds HeapSnapshot, HeapSnapshotBuilder, and HeapProfiler.

HeapProfiler hangs off of the VM and holds the list of snapshots.
I expect to add other HeapProfiling features, such as allocation
tracking, to the profiler.

HeapSnapshot contains a collection of live cells and their identifiers.
It can point to a previous HeapSnapshot, to ensure that a cell that
already received an identifier maintains the same identifier across
multiple snapshots. When a snapshotted cell gets garbage collected,
the cell will be swept from the HeapSnapshot at the end of collection
to ensure the list contains only live cells.

When building a HeapSnapshot nodes are added in increasing node
identifier order. When done building, the list of nodes is complete
and the snapshot is finalized. At this point the nodes are sorted
by JSCell* address to allow for quick lookup of a JSCell*.

HeapSnapshotBuilder is where snapshotting begins. The builder
will initiate a specialized heap snapshotting garbage collection.
During this collection the builder will be notified of all marked
(live) cells, and connections between cells, as seen by SlotVisitors.
The builder can reference the previous, readonly, HeapSnapshots to
avoid creating new nodes for cells that have already been snapshotted.
When it is determined that we are visiting a live cell for the first
time, we give the cell a unique identifier and add it to the the
snapshot we are building.

Since edge data is costly, and of little long term utility, this
data is only held by the builder for serialization, and not stored
long term with the HeapSnapshot node data.

The goals of HeapSnapshotting at this time are:
- minimal impact on performance when not profiling the heap
- unique identifier for cells, so they may be identified across multiple snapshots
- nodes and edges to be able to construct a graph of which nodes reference/retain which other nodes
- node data - identifier, type (class name), size
- edge data - from cell, to cell, type / data (to come in a follow-up patch)

* CMakeLists.txt:
* _javascript_Core.xcodeproj/project.pbxproj:
* _javascript_Core.vcxproj/_javascript_Core.vcxproj:
* _javascript_Core.vcxproj/_javascript_Core.vcxproj.filters:
Add new files to the build.

* heap/Heap.cpp:
(JSC::Heap::isHeapSnapshotting):
(JSC::RemoveDeadHeapSnapshotNodes::RemoveDeadHeapSnapshotNodes):
(JSC::RemoveDeadHeapSnapshotNodes::operator()):
(JSC::Heap::removeDeadHeapSnapshotNodes):
(JSC::Heap::collectImpl):
After every collection, sweep dead cells from in memory snapshots.

* runtime/VM.cpp:
(JSC::VM::ensureHeapProfiler):
* runtime/VM.h:
(JSC::VM::heapProfiler):
* heap/Heap.h:
* heap/HeapProfiler.cpp: Added.
(JSC::HeapProfiler::HeapProfiler):
(JSC::HeapProfiler::~HeapProfiler):
(JSC::HeapProfiler::mostRecentSnapshot):
(JSC::HeapProfiler::appendSnapshot):
(JSC::HeapProfiler::clearSnapshots):
(JSC::HeapProfiler::setActiveSnapshotBuilder):
* heap/HeapProfiler.h: Added.
(JSC::HeapProfiler::vm):
(JSC::HeapProfiler::activeSnapshotBuilder):
VM and Heap can look at the profiler to determine if we are building a
snapshot, or the "head" snapshot to use for sweeping.

* heap/HeapSnapshot.cpp: Added.
(JSC::HeapSnapshot::HeapSnapshot):
(JSC::HeapSnapshot::~HeapSnapshot):
(JSC::HeapSnapshot::appendNode):
Add a node to the unfinalized list of new cells.

(JSC::HeapSnapshot::sweepCell):
(JSC::HeapSnapshot::shrinkToFit):
Collect a list of cells for sweeping and then remove them all at once
in shrinkToFit. This is done to avoid thrashing of individual removes
that could cause many overlapping moves within the Vector.

(JSC::HeapSnapshot::finalize):
Sort the list, and also cache the bounding start/stop identifiers.
No other snapshot can contain an identifier in this range, so it will
improve lookup of a node from an identifier.

(JSC::HeapSnapshot::nodeForCell):
(JSC::HeapSnapshot::nodeForObjectIdentifier):
Search helpers.

* heap/HeapSnapshotBuilder.h: Added.
(JSC::HeapSnapshotNode::HeapSnapshotNode):
(JSC::HeapSnapshotEdge::HeapSnapshotEdge):
Node and Edge struct types the builder creates.

* heap/HeapSnapshotBuilder.cpp: Added.
(JSC::HeapSnapshotBuilder::getNextObjectIdentifier):
(JSC::HeapSnapshotBuilder::HeapSnapshotBuilder):
(JSC::HeapSnapshotBuilder::~HeapSnapshotBuilder):
(JSC::HeapSnapshotBuilder::buildSnapshot):
(JSC::HeapSnapshotBuilder::appendNode):
(JSC::HeapSnapshotBuilder::appendEdge):
When building the snapshot, generating the next identifier, and
appending to any of the lists must be guarded by a lock because
SlotVisitors running in parallel may be accessing the builder.

(JSC::HeapSnapshotBuilder::hasExistingNodeForCell):
Looking up if a node already exists in a previous snapshot can be
done without a lock because at this point the data is readonly.

(JSC::edgeTypeToNumber):
(JSC::edgeTypeToString):
(JSC::HeapSnapshotBuilder::json):
JSON serialization of a heap snapshot contains node and edge data.

* heap/SlotVisitor.h:
* heap/SlotVisitor.cpp:
(JSC::SlotVisitor::didStartMarking):
(JSC::SlotVisitor::reset):
Set/clear the active snapshot builder to know if this will be a
snapshotting GC or not.

(JSC::SlotVisitor::append):
(JSC::SlotVisitor::setMarkedAndAppendToMarkStack):
Inform the builder of a new node or edge.

(JSC::SlotVisitor::visitChildren):
Remember the current cell we are visiting so that if we need to
inform the builder of edges we know the "from" cell.

* jsc.cpp:
(SimpleObject::SimpleObject):
(SimpleObject::create):
(SimpleObject::finishCreation):
(SimpleObject::visitChildren):
(SimpleObject::createStructure):
(SimpleObject::hiddenValue):
(SimpleObject::setHiddenValue):
Create a new class "SimpleObject" that can be used by heap snapshotting
tests. It is easy to filter for this new class name and test internal
edge relationships created by garbage collection visiting the cell.

(functionCreateSimpleObject):
(functionGetHiddenValue):
(functionSetHiddenValue):
Expose methods to create and interact with a SimpleObject.

(functionGenerateHeapSnapshot):
Expose methods to create a heap snapshot. This currently automatically
turns the serialized string into a JSON object. That may change.

* tests/heapProfiler.yaml: Added.
* tests/heapProfiler/basic-edges.js: Added.
(excludeStructure):
* tests/heapProfiler/basic-nodes.js: Added.
(hasDifferentSizeNodes):
(hasAllInternalNodes):
Add tests for basic node and edge data.

* tests/heapProfiler/driver/driver.js: Added.
(assert):
(CheapHeapSnapshotNode):
(CheapHeapSnapshotEdge):
(CheapHeapSnapshotEdge.prototype.get from):
(CheapHeapSnapshotEdge.prototype.get to):
(CheapHeapSnapshot):
(CheapHeapSnapshot.prototype.get nodes):
(CheapHeapSnapshot.prototype.get edges):
(CheapHeapSnapshot.prototype.nodeWithIdentifier):
(CheapHeapSnapshot.prototype.nodesWithClassName):
(CheapHeapSnapshot.prototype.classNameFromTableIndex):
(CheapHeapSnapshot.prototype.edgeTypeFromTableIndex):
(createCheapHeapSnapshot):
(HeapSnapshotNode):
(HeapSnapshotEdge):
(HeapSnapshot):
(HeapSnapshot.prototype.nodesWithClassName):
(createHeapSnapshot):
Add two HeapSnapshot representations.
CheapHeapSnapshot creates two lists of node and edge data that
lazily creates objects as needed.
HeapSnapshot creates an object for each node and edge. This
is wasteful but easier to use.

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/CMakeLists.txt (197488 => 197489)


--- trunk/Source/_javascript_Core/CMakeLists.txt	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/CMakeLists.txt	2016-03-03 05:15:56 UTC (rev 197489)
@@ -443,6 +443,9 @@
     heap/HandleStack.cpp
     heap/Heap.cpp
     heap/HeapHelperPool.cpp
+    heap/HeapProfiler.cpp
+    heap/HeapSnapshot.cpp
+    heap/HeapSnapshotBuilder.cpp
     heap/HeapStatistics.cpp
     heap/HeapTimer.cpp
     heap/HeapVerifier.cpp

Modified: trunk/Source/_javascript_Core/ChangeLog (197488 => 197489)


--- trunk/Source/_javascript_Core/ChangeLog	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-03-03 05:15:56 UTC (rev 197489)
@@ -1,3 +1,196 @@
+2016-03-02  Joseph Pecoraro  <[email protected]>
+
+        Add ability to generate a Heap Snapshot
+        https://bugs.webkit.org/show_bug.cgi?id=154847
+
+        Reviewed by Mark Lam.
+
+        This adds HeapSnapshot, HeapSnapshotBuilder, and HeapProfiler.
+
+        HeapProfiler hangs off of the VM and holds the list of snapshots.
+        I expect to add other HeapProfiling features, such as allocation
+        tracking, to the profiler.
+
+        HeapSnapshot contains a collection of live cells and their identifiers.
+        It can point to a previous HeapSnapshot, to ensure that a cell that
+        already received an identifier maintains the same identifier across
+        multiple snapshots. When a snapshotted cell gets garbage collected,
+        the cell will be swept from the HeapSnapshot at the end of collection
+        to ensure the list contains only live cells.
+
+        When building a HeapSnapshot nodes are added in increasing node
+        identifier order. When done building, the list of nodes is complete
+        and the snapshot is finalized. At this point the nodes are sorted
+        by JSCell* address to allow for quick lookup of a JSCell*.
+
+        HeapSnapshotBuilder is where snapshotting begins. The builder
+        will initiate a specialized heap snapshotting garbage collection.
+        During this collection the builder will be notified of all marked
+        (live) cells, and connections between cells, as seen by SlotVisitors.
+        The builder can reference the previous, readonly, HeapSnapshots to
+        avoid creating new nodes for cells that have already been snapshotted.
+        When it is determined that we are visiting a live cell for the first
+        time, we give the cell a unique identifier and add it to the the
+        snapshot we are building.
+
+        Since edge data is costly, and of little long term utility, this
+        data is only held by the builder for serialization, and not stored
+        long term with the HeapSnapshot node data.
+
+        The goals of HeapSnapshotting at this time are:
+        - minimal impact on performance when not profiling the heap
+        - unique identifier for cells, so they may be identified across multiple snapshots
+        - nodes and edges to be able to construct a graph of which nodes reference/retain which other nodes
+        - node data - identifier, type (class name), size
+        - edge data - from cell, to cell, type / data (to come in a follow-up patch)
+
+        * CMakeLists.txt:
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * _javascript_Core.vcxproj/_javascript_Core.vcxproj:
+        * _javascript_Core.vcxproj/_javascript_Core.vcxproj.filters:
+        Add new files to the build.
+
+        * heap/Heap.cpp:
+        (JSC::Heap::isHeapSnapshotting):
+        (JSC::RemoveDeadHeapSnapshotNodes::RemoveDeadHeapSnapshotNodes):
+        (JSC::RemoveDeadHeapSnapshotNodes::operator()):
+        (JSC::Heap::removeDeadHeapSnapshotNodes):
+        (JSC::Heap::collectImpl):
+        After every collection, sweep dead cells from in memory snapshots.
+
+        * runtime/VM.cpp:
+        (JSC::VM::ensureHeapProfiler):
+        * runtime/VM.h:
+        (JSC::VM::heapProfiler):
+        * heap/Heap.h:
+        * heap/HeapProfiler.cpp: Added.
+        (JSC::HeapProfiler::HeapProfiler):
+        (JSC::HeapProfiler::~HeapProfiler):
+        (JSC::HeapProfiler::mostRecentSnapshot):
+        (JSC::HeapProfiler::appendSnapshot):
+        (JSC::HeapProfiler::clearSnapshots):
+        (JSC::HeapProfiler::setActiveSnapshotBuilder):
+        * heap/HeapProfiler.h: Added.
+        (JSC::HeapProfiler::vm):
+        (JSC::HeapProfiler::activeSnapshotBuilder):
+        VM and Heap can look at the profiler to determine if we are building a
+        snapshot, or the "head" snapshot to use for sweeping.
+
+        * heap/HeapSnapshot.cpp: Added.
+        (JSC::HeapSnapshot::HeapSnapshot):
+        (JSC::HeapSnapshot::~HeapSnapshot):
+        (JSC::HeapSnapshot::appendNode):
+        Add a node to the unfinalized list of new cells.
+
+        (JSC::HeapSnapshot::sweepCell):
+        (JSC::HeapSnapshot::shrinkToFit):
+        Collect a list of cells for sweeping and then remove them all at once
+        in shrinkToFit. This is done to avoid thrashing of individual removes
+        that could cause many overlapping moves within the Vector.
+
+        (JSC::HeapSnapshot::finalize):
+        Sort the list, and also cache the bounding start/stop identifiers.
+        No other snapshot can contain an identifier in this range, so it will
+        improve lookup of a node from an identifier.
+
+        (JSC::HeapSnapshot::nodeForCell):
+        (JSC::HeapSnapshot::nodeForObjectIdentifier):
+        Search helpers.
+
+        * heap/HeapSnapshotBuilder.h: Added.
+        (JSC::HeapSnapshotNode::HeapSnapshotNode):
+        (JSC::HeapSnapshotEdge::HeapSnapshotEdge):
+        Node and Edge struct types the builder creates.
+
+        * heap/HeapSnapshotBuilder.cpp: Added.
+        (JSC::HeapSnapshotBuilder::getNextObjectIdentifier):
+        (JSC::HeapSnapshotBuilder::HeapSnapshotBuilder):
+        (JSC::HeapSnapshotBuilder::~HeapSnapshotBuilder):
+        (JSC::HeapSnapshotBuilder::buildSnapshot):
+        (JSC::HeapSnapshotBuilder::appendNode):
+        (JSC::HeapSnapshotBuilder::appendEdge):
+        When building the snapshot, generating the next identifier, and
+        appending to any of the lists must be guarded by a lock because
+        SlotVisitors running in parallel may be accessing the builder.
+
+        (JSC::HeapSnapshotBuilder::hasExistingNodeForCell):
+        Looking up if a node already exists in a previous snapshot can be
+        done without a lock because at this point the data is readonly.
+
+        (JSC::edgeTypeToNumber):
+        (JSC::edgeTypeToString):
+        (JSC::HeapSnapshotBuilder::json):
+        JSON serialization of a heap snapshot contains node and edge data.
+
+        * heap/SlotVisitor.h:
+        * heap/SlotVisitor.cpp:
+        (JSC::SlotVisitor::didStartMarking):
+        (JSC::SlotVisitor::reset):
+        Set/clear the active snapshot builder to know if this will be a
+        snapshotting GC or not.
+
+        (JSC::SlotVisitor::append):
+        (JSC::SlotVisitor::setMarkedAndAppendToMarkStack):
+        Inform the builder of a new node or edge.
+
+        (JSC::SlotVisitor::visitChildren):
+        Remember the current cell we are visiting so that if we need to
+        inform the builder of edges we know the "from" cell.
+
+        * jsc.cpp:
+        (SimpleObject::SimpleObject):
+        (SimpleObject::create):
+        (SimpleObject::finishCreation):
+        (SimpleObject::visitChildren):
+        (SimpleObject::createStructure):
+        (SimpleObject::hiddenValue):
+        (SimpleObject::setHiddenValue):
+        Create a new class "SimpleObject" that can be used by heap snapshotting
+        tests. It is easy to filter for this new class name and test internal
+        edge relationships created by garbage collection visiting the cell.
+
+        (functionCreateSimpleObject):
+        (functionGetHiddenValue):
+        (functionSetHiddenValue):
+        Expose methods to create and interact with a SimpleObject.
+
+        (functionGenerateHeapSnapshot):
+        Expose methods to create a heap snapshot. This currently automatically
+        turns the serialized string into a JSON object. That may change.
+
+        * tests/heapProfiler.yaml: Added.
+        * tests/heapProfiler/basic-edges.js: Added.
+        (excludeStructure):
+        * tests/heapProfiler/basic-nodes.js: Added.
+        (hasDifferentSizeNodes):
+        (hasAllInternalNodes):
+        Add tests for basic node and edge data.
+
+        * tests/heapProfiler/driver/driver.js: Added.
+        (assert):
+        (CheapHeapSnapshotNode):
+        (CheapHeapSnapshotEdge):
+        (CheapHeapSnapshotEdge.prototype.get from):
+        (CheapHeapSnapshotEdge.prototype.get to):
+        (CheapHeapSnapshot):
+        (CheapHeapSnapshot.prototype.get nodes):
+        (CheapHeapSnapshot.prototype.get edges):
+        (CheapHeapSnapshot.prototype.nodeWithIdentifier):
+        (CheapHeapSnapshot.prototype.nodesWithClassName):
+        (CheapHeapSnapshot.prototype.classNameFromTableIndex):
+        (CheapHeapSnapshot.prototype.edgeTypeFromTableIndex):
+        (createCheapHeapSnapshot):
+        (HeapSnapshotNode):
+        (HeapSnapshotEdge):
+        (HeapSnapshot):
+        (HeapSnapshot.prototype.nodesWithClassName):
+        (createHeapSnapshot):
+        Add two HeapSnapshot representations.
+        CheapHeapSnapshot creates two lists of node and edge data that
+        lazily creates objects as needed.
+        HeapSnapshot creates an object for each node and edge. This
+        is wasteful but easier to use.
+
 2016-03-02  Filip Pizlo  <[email protected]>
 
         RegExpPrototype should check for exceptions after calling toString and doing so should not be expensive

Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj (197488 => 197489)


--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj	2016-03-03 05:15:56 UTC (rev 197489)
@@ -575,6 +575,9 @@
     <ClCompile Include="..\heap\HandleStack.cpp" />
     <ClCompile Include="..\heap\Heap.cpp" />
     <ClCompile Include="..\heap\HeapHelperPool.cpp" />
+    <ClCompile Include="..\heap\HeapProfiler.cpp" />
+    <ClCompile Include="..\heap\HeapSnapshot.cpp" />
+    <ClCompile Include="..\heap\HeapSnapshotBuilder.cpp" />
     <ClCompile Include="..\heap\HeapStatistics.cpp" />
     <ClCompile Include="..\heap\HeapTimer.cpp" />
     <ClCompile Include="..\heap\HeapVerifier.cpp" />
@@ -1386,6 +1389,9 @@
     <ClInclude Include="..\heap\HeapInlines.h" />
     <ClInclude Include="..\heap\HeapOperation.h" />
     <ClInclude Include="..\heap\HeapRootVisitor.h" />
+    <ClInclude Include="..\heap\HeapProfiler.h" />
+    <ClInclude Include="..\heap\HeapSnapshot.h" />
+    <ClInclude Include="..\heap\HeapSnapshotBuilder.h" />
     <ClInclude Include="..\heap\HeapStatistics.h" />
     <ClInclude Include="..\heap\HeapTimer.h" />
     <ClInclude Include="..\heap\HeapVerifier.h" />

Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters (197488 => 197489)


--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters	2016-03-03 05:15:56 UTC (rev 197489)
@@ -294,6 +294,15 @@
     <ClCompile Include="..\heap\HeapHelperPool.cpp">
       <Filter>heap</Filter>
     </ClCompile>
+    <ClCompile Include="..\heap\HeapProfiler.cpp">
+      <Filter>heap</Filter>
+    </ClCompile>
+    <ClCompile Include="..\heap\HeapSnapshot.cpp">
+      <Filter>heap</Filter>
+    </ClCompile>
+    <ClCompile Include="..\heap\HeapSnapshotBuilder.cpp">
+      <Filter>heap</Filter>
+    </ClCompile>
     <ClCompile Include="..\heap\HeapStatistics.cpp">
       <Filter>heap</Filter>
     </ClCompile>
@@ -2368,6 +2377,15 @@
     <ClInclude Include="..\heap\HeapRootVisitor.h">
       <Filter>heap</Filter>
     </ClInclude>
+    <ClInclude Include="..\heap\HeapProfiler.h">
+      <Filter>heap</Filter>
+    </ClInclude>
+    <ClInclude Include="..\heap\HeapSnapshot.h">
+      <Filter>heap</Filter>
+    </ClInclude>
+    <ClInclude Include="..\heap\HeapSnapshotBuilder.h">
+      <Filter>heap</Filter>
+    </ClInclude>
     <ClInclude Include="..\heap\HeapStatistics.h">
       <Filter>heap</Filter>
     </ClInclude>

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (197488 => 197489)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-03-03 05:15:56 UTC (rev 197489)
@@ -1548,6 +1548,8 @@
 		A513E5CB185F9624007E95AD /* InjectedScriptManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A513E5C9185F9624007E95AD /* InjectedScriptManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A514B2C2185A684400F3C7CB /* InjectedScriptBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A514B2C0185A684400F3C7CB /* InjectedScriptBase.cpp */; };
 		A514B2C3185A684400F3C7CB /* InjectedScriptBase.h in Headers */ = {isa = PBXBuildFile; fileRef = A514B2C1185A684400F3C7CB /* InjectedScriptBase.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		A5311C361C77CEC500E6B1B6 /* HeapSnapshotBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		A5311C371C77CECA00E6B1B6 /* HeapSnapshotBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */; };
 		A532438718568335002ED692 /* InspectorBackendDispatchers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A532438118568317002ED692 /* InspectorBackendDispatchers.cpp */; };
 		A532438818568335002ED692 /* InspectorBackendDispatchers.h in Headers */ = {isa = PBXBuildFile; fileRef = A532438218568317002ED692 /* InspectorBackendDispatchers.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A532438918568335002ED692 /* InspectorFrontendDispatchers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A532438318568317002ED692 /* InspectorFrontendDispatchers.cpp */; };
@@ -1558,6 +1560,8 @@
 		A5339EC61BB399A60054F005 /* InspectorHeapAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A5339EC51BB399900054F005 /* InspectorHeapAgent.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A5339EC71BB399A90054F005 /* InspectorHeapAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5339EC41BB399900054F005 /* InspectorHeapAgent.cpp */; };
 		A5339EC91BB4B4600054F005 /* HeapObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = A5339EC81BB4B4510054F005 /* HeapObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		A5398FAB1C750DA40060A963 /* HeapProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = A5398FAA1C750D950060A963 /* HeapProfiler.h */; };
+		A5398FAC1C750DA60060A963 /* HeapProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5398FA91C750D950060A963 /* HeapProfiler.cpp */; };
 		A53CE08518BC1A5600BEDF76 /* ConsolePrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A53CE08118BC1A5600BEDF76 /* ConsolePrototype.cpp */; };
 		A53CE08618BC1A5600BEDF76 /* ConsolePrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = A53CE08218BC1A5600BEDF76 /* ConsolePrototype.h */; };
 		A53CE08718BC1A5600BEDF76 /* JSConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A53CE08318BC1A5600BEDF76 /* JSConsole.cpp */; };
@@ -1566,6 +1570,8 @@
 		A53F1AC018C90F8F0072EB6D /* framework.sb in Resources */ = {isa = PBXBuildFile; fileRef = A53F1ABE18C90EC70072EB6D /* framework.sb */; };
 		A54982031891D0B00081E5B8 /* EventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54982011891D0B00081E5B8 /* EventLoop.cpp */; };
 		A54982041891D0B00081E5B8 /* EventLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = A54982021891D0B00081E5B8 /* EventLoop.h */; };
+		A54C2AB01C6544EE00A18D78 /* HeapSnapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54C2AAE1C6544D100A18D78 /* HeapSnapshot.cpp */; };
+		A54C2AB11C6544F200A18D78 /* HeapSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = A54C2AAF1C6544D100A18D78 /* HeapSnapshot.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A54CF2F5184EAB2400237F19 /* ScriptValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54CF2F2184EAB2400237F19 /* ScriptValue.cpp */; };
 		A54CF2F6184EAB2400237F19 /* ScriptValue.h in Headers */ = {isa = PBXBuildFile; fileRef = A54CF2F3184EAB2400237F19 /* ScriptValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		A54CF2F9184EAEDA00237F19 /* ScriptObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54CF2F7184EAEDA00237F19 /* ScriptObject.cpp */; };
@@ -3743,6 +3749,8 @@
 		A513E5C9185F9624007E95AD /* InjectedScriptManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptManager.h; sourceTree = "<group>"; };
 		A514B2C0185A684400F3C7CB /* InjectedScriptBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InjectedScriptBase.cpp; sourceTree = "<group>"; };
 		A514B2C1185A684400F3C7CB /* InjectedScriptBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptBase.h; sourceTree = "<group>"; };
+		A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapSnapshotBuilder.cpp; sourceTree = "<group>"; };
+		A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapSnapshotBuilder.h; sourceTree = "<group>"; };
 		A532438118568317002ED692 /* InspectorBackendDispatchers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorBackendDispatchers.cpp; sourceTree = "<group>"; };
 		A532438218568317002ED692 /* InspectorBackendDispatchers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InspectorBackendDispatchers.h; sourceTree = "<group>"; };
 		A532438318568317002ED692 /* InspectorFrontendDispatchers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorFrontendDispatchers.cpp; sourceTree = "<group>"; };
@@ -3755,6 +3763,8 @@
 		A5339EC41BB399900054F005 /* InspectorHeapAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorHeapAgent.cpp; sourceTree = "<group>"; };
 		A5339EC51BB399900054F005 /* InspectorHeapAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorHeapAgent.h; sourceTree = "<group>"; };
 		A5339EC81BB4B4510054F005 /* HeapObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapObserver.h; sourceTree = "<group>"; };
+		A5398FA91C750D950060A963 /* HeapProfiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapProfiler.cpp; sourceTree = "<group>"; };
+		A5398FAA1C750D950060A963 /* HeapProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapProfiler.h; sourceTree = "<group>"; };
 		A53CE08118BC1A5600BEDF76 /* ConsolePrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConsolePrototype.cpp; sourceTree = "<group>"; };
 		A53CE08218BC1A5600BEDF76 /* ConsolePrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConsolePrototype.h; sourceTree = "<group>"; };
 		A53CE08318BC1A5600BEDF76 /* JSConsole.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSConsole.cpp; sourceTree = "<group>"; };
@@ -3763,6 +3773,8 @@
 		A53F1ABE18C90EC70072EB6D /* framework.sb */ = {isa = PBXFileReference; lastKnownFileType = text; path = framework.sb; sourceTree = "<group>"; };
 		A54982011891D0B00081E5B8 /* EventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventLoop.cpp; sourceTree = "<group>"; };
 		A54982021891D0B00081E5B8 /* EventLoop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventLoop.h; sourceTree = "<group>"; };
+		A54C2AAE1C6544D100A18D78 /* HeapSnapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapSnapshot.cpp; sourceTree = "<group>"; };
+		A54C2AAF1C6544D100A18D78 /* HeapSnapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapSnapshot.h; sourceTree = "<group>"; };
 		A54CF2F2184EAB2400237F19 /* ScriptValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptValue.cpp; sourceTree = "<group>"; };
 		A54CF2F3184EAB2400237F19 /* ScriptValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptValue.h; sourceTree = "<group>"; };
 		A54CF2F7184EAEDA00237F19 /* ScriptObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptObject.cpp; sourceTree = "<group>"; };
@@ -5093,7 +5105,13 @@
 				2AD8932917E3868F00668276 /* HeapIterationScope.h */,
 				A5339EC81BB4B4510054F005 /* HeapObserver.h */,
 				2A6F462517E959CE00C45C98 /* HeapOperation.h */,
+				A5398FA91C750D950060A963 /* HeapProfiler.cpp */,
+				A5398FAA1C750D950060A963 /* HeapProfiler.h */,
 				14F97446138C853E00DA1C67 /* HeapRootVisitor.h */,
+				A54C2AAE1C6544D100A18D78 /* HeapSnapshot.cpp */,
+				A54C2AAF1C6544D100A18D78 /* HeapSnapshot.h */,
+				A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */,
+				A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */,
 				C24D31E0161CD695002AA4DB /* HeapStatistics.cpp */,
 				C24D31E1161CD695002AA4DB /* HeapStatistics.h */,
 				C2E526BB1590EF000054E48D /* HeapTimer.cpp */,
@@ -6918,6 +6936,7 @@
 				BC18C3E60E16F5CD00B34460 /* ArrayConstructor.h in Headers */,
 				996B73171BDA067F00331B84 /* ArrayConstructor.lut.h in Headers */,
 				0FB7F39515ED8E4600F167B2 /* ArrayConventions.h in Headers */,
+				A5311C361C77CEC500E6B1B6 /* HeapSnapshotBuilder.h in Headers */,
 				A7BDAEC917F4EA1400F6140C /* ArrayIteratorPrototype.h in Headers */,
 				996B73181BDA068000331B84 /* ArrayIteratorPrototype.lut.h in Headers */,
 				0F63945515D07057006A597C /* ArrayProfile.h in Headers */,
@@ -7090,6 +7109,7 @@
 				41359CF30FDD89AD00206180 /* DateConversion.h in Headers */,
 				BC1166020E1997B4008066DD /* DateInstance.h in Headers */,
 				14A1563210966365006FA260 /* DateInstanceCache.h in Headers */,
+				A54C2AB11C6544F200A18D78 /* HeapSnapshot.h in Headers */,
 				BCD2034C0E17135E002C7E82 /* DatePrototype.h in Headers */,
 				BCD203E80E1718F4002C7E82 /* DatePrototype.lut.h in Headers */,
 				BC18C3FA0E16F5CD00B34460 /* Debugger.h in Headers */,
@@ -7502,6 +7522,7 @@
 				FE187A0F1C030D6C0038BBCA /* SnippetOperand.h in Headers */,
 				A1587D701B4DC14100D69849 /* IntlDateTimeFormatConstructor.h in Headers */,
 				A1587D751B4DC1C600D69849 /* IntlDateTimeFormatConstructor.lut.h in Headers */,
+				A5398FAB1C750DA40060A963 /* HeapProfiler.h in Headers */,
 				A1587D721B4DC14100D69849 /* IntlDateTimeFormatPrototype.h in Headers */,
 				A1587D761B4DC1C600D69849 /* IntlDateTimeFormatPrototype.lut.h in Headers */,
 				A1D792FD1B43864B004516F5 /* IntlNumberFormat.h in Headers */,
@@ -8822,6 +8843,7 @@
 				0FE7211D193B9C590031F6ED /* DFGTransition.cpp in Sources */,
 				0F63944015C75F1D006A597C /* DFGTypeCheckHoistingPhase.cpp in Sources */,
 				0FBE0F7616C1DB0F0082C5E8 /* DFGUnificationPhase.cpp in Sources */,
+				A5398FAC1C750DA60060A963 /* HeapProfiler.cpp in Sources */,
 				0F34B14916D42010001CDA5A /* DFGUseKind.cpp in Sources */,
 				0F3B3A2B15475000003ED0FF /* DFGValidate.cpp in Sources */,
 				0F2BDC4F15228BF300CD8910 /* DFGValueSource.cpp in Sources */,
@@ -8995,6 +9017,7 @@
 				0F2B66E417B6B5AB00A7AE3F /* JSArrayBufferConstructor.cpp in Sources */,
 				0F2B66E617B6B5AB00A7AE3F /* JSArrayBufferPrototype.cpp in Sources */,
 				0F2B66E817B6B5AB00A7AE3F /* JSArrayBufferView.cpp in Sources */,
+				A5311C371C77CECA00E6B1B6 /* HeapSnapshotBuilder.cpp in Sources */,
 				A7BDAECA17F4EA1400F6140C /* JSArrayIterator.cpp in Sources */,
 				1421359B0A677F4F00A8195E /* JSBase.cpp in Sources */,
 				86FA9E91142BBB2E001773B7 /* JSBoundFunction.cpp in Sources */,
@@ -9247,6 +9270,7 @@
 				BCDE3B430E6C832D001453A7 /* Structure.cpp in Sources */,
 				7E4EE70F0EBB7A5B005934AA /* StructureChain.cpp in Sources */,
 				2AF7382C18BBBF92008A5A37 /* StructureIDTable.cpp in Sources */,
+				A54C2AB01C6544EE00A18D78 /* HeapSnapshot.cpp in Sources */,
 				C2F0F2D116BAEEE900187C19 /* StructureRareData.cpp in Sources */,
 				0FB438A319270B1D00E1FBC9 /* StructureSet.cpp in Sources */,
 				0F766D3815AE4A1C008F363E /* StructureStubClearingWatchpoint.cpp in Sources */,

Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (197488 => 197489)


--- trunk/Source/_javascript_Core/heap/Heap.cpp	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp	2016-03-03 05:15:56 UTC (rev 197489)
@@ -33,7 +33,9 @@
 #include "GCIncomingRefCountedSetInlines.h"
 #include "HeapHelperPool.h"
 #include "HeapIterationScope.h"
+#include "HeapProfiler.h"
 #include "HeapRootVisitor.h"
+#include "HeapSnapshot.h"
 #include "HeapStatistics.h"
 #include "HeapVerifier.h"
 #include "IncrementalSweeper.h"
@@ -758,6 +760,43 @@
 #endif
 }
 
+bool Heap::isHeapSnapshotting() const
+{
+    HeapProfiler* heapProfiler = m_vm->heapProfiler();
+    if (UNLIKELY(heapProfiler))
+        return heapProfiler->activeSnapshotBuilder();
+    return false;
+}
+
+struct RemoveDeadHeapSnapshotNodes : MarkedBlock::CountFunctor {
+    RemoveDeadHeapSnapshotNodes(HeapSnapshot& snapshot)
+        : m_snapshot(snapshot)
+    {
+    }
+
+    IterationStatus operator()(JSCell* cell)
+    {
+        m_snapshot.sweepCell(cell);
+        return IterationStatus::Continue;
+    }
+
+    HeapSnapshot& m_snapshot;
+};
+
+void Heap::removeDeadHeapSnapshotNodes()
+{
+    GCPHASE(RemoveDeadHeapSnapshotNodes);
+    HeapProfiler* heapProfiler = m_vm->heapProfiler();
+    if (UNLIKELY(heapProfiler)) {
+        if (HeapSnapshot* snapshot = heapProfiler->mostRecentSnapshot()) {
+            HeapIterationScope heapIterationScope(*this);
+            RemoveDeadHeapSnapshotNodes functor(*snapshot);
+            m_objectSpace.forEachDeadCell(heapIterationScope, functor);
+            snapshot->shrinkToFit();
+        }
+    }
+}
+
 void Heap::visitProtectedObjects(HeapRootVisitor& heapRootVisitor)
 {
     GCPHASE(VisitProtectedObjects);
@@ -1124,6 +1163,7 @@
     removeDeadCompilerWorklistEntries();
     deleteUnmarkedCompiledCode();
     deleteSourceProviderCaches();
+    removeDeadHeapSnapshotNodes();
     notifyIncrementalSweeper();
     writeBarrierCurrentlyExecutingCodeBlocks();
 

Modified: trunk/Source/_javascript_Core/heap/Heap.h (197488 => 197489)


--- trunk/Source/_javascript_Core/heap/Heap.h	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/heap/Heap.h	2016-03-03 05:15:56 UTC (rev 197489)
@@ -163,6 +163,8 @@
     void notifyIsSafeToCollect() { m_isSafeToCollect = true; }
     bool isSafeToCollect() const { return m_isSafeToCollect; }
 
+    JS_EXPORT_PRIVATE bool isHeapSnapshotting() const;
+
     JS_EXPORT_PRIVATE void collectAllGarbageIfNotDoneRecently();
     void collectAllGarbage() { collectAndSweep(FullCollection); }
     JS_EXPORT_PRIVATE void collectAndSweep(HeapOperation collectionType = AnyCollection);
@@ -327,6 +329,7 @@
     void sweepArrayBuffers();
     void snapshotMarkedSpace();
     void deleteSourceProviderCaches();
+    void removeDeadHeapSnapshotNodes();
     void notifyIncrementalSweeper();
     void writeBarrierCurrentlyExecutingCodeBlocks();
     void resetAllocators();

Added: trunk/Source/_javascript_Core/heap/HeapProfiler.cpp (0 => 197489)


--- trunk/Source/_javascript_Core/heap/HeapProfiler.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapProfiler.cpp	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "HeapProfiler.h"
+
+#include "HeapSnapshot.h"
+#include "VM.h"
+
+namespace JSC {
+
+HeapProfiler::HeapProfiler(VM& vm)
+    : m_vm(vm)
+{
+}
+
+HeapProfiler::~HeapProfiler()
+{
+}
+
+HeapSnapshot* HeapProfiler::mostRecentSnapshot()
+{
+    if (m_snapshots.isEmpty())
+        return nullptr;
+    return m_snapshots.last().get();
+}
+
+void HeapProfiler::appendSnapshot(std::unique_ptr<HeapSnapshot> snapshot)
+{
+    m_snapshots.append(WTFMove(snapshot));
+}
+
+void HeapProfiler::clearSnapshots()
+{
+    m_snapshots.clear();
+}
+
+void HeapProfiler::setActiveSnapshotBuilder(HeapSnapshotBuilder* builder)
+{
+    ASSERT(!!m_activeBuilder != !!builder);
+    m_activeBuilder = builder;
+}
+
+} // namespace JSC

Added: trunk/Source/_javascript_Core/heap/HeapProfiler.h (0 => 197489)


--- trunk/Source/_javascript_Core/heap/HeapProfiler.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapProfiler.h	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef HeapProfiler_h
+#define HeapProfiler_h
+
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class HeapSnapshot;
+class HeapSnapshotBuilder;
+class VM;
+
+class HeapProfiler {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    HeapProfiler(VM&);
+    ~HeapProfiler();
+
+    VM& vm() const { return m_vm; }
+
+    HeapSnapshot* mostRecentSnapshot();
+    void appendSnapshot(std::unique_ptr<HeapSnapshot>);
+    void clearSnapshots();
+
+    HeapSnapshotBuilder* activeSnapshotBuilder() const { return m_activeBuilder; }
+    void setActiveSnapshotBuilder(HeapSnapshotBuilder*);
+
+private:
+    VM& m_vm;
+    Vector<std::unique_ptr<HeapSnapshot>> m_snapshots;
+    HeapSnapshotBuilder* m_activeBuilder { nullptr };
+};
+
+} // namespace JSC
+
+#endif // HeapProfiler_h 

Added: trunk/Source/_javascript_Core/heap/HeapSnapshot.cpp (0 => 197489)


--- trunk/Source/_javascript_Core/heap/HeapSnapshot.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshot.cpp	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "HeapSnapshot.h"
+
+#include "JSCInlines.h"
+
+namespace JSC {
+
+HeapSnapshot::HeapSnapshot(HeapSnapshot* previousSnapshot)
+    : m_previous(previousSnapshot)
+{
+}
+
+HeapSnapshot::~HeapSnapshot()
+{
+}
+
+void HeapSnapshot::appendNode(const HeapSnapshotNode& node)
+{
+    ASSERT(!m_finalized);
+    ASSERT(!m_previous || !m_previous->nodeForCell(node.cell));
+
+    m_nodes.append(node);
+}
+
+void HeapSnapshot::sweepCell(JSCell* cell)
+{
+    ASSERT(cell);
+
+    if (m_finalized && !isEmpty()) {
+        unsigned start = 0;
+        unsigned end = m_nodes.size();
+        while (start != end) {
+            unsigned middle = start + ((end - start) / 2);
+            HeapSnapshotNode& node = m_nodes[middle];
+            if (cell == node.cell) {
+                // Cells should always have 0 as low bits.
+                // Mark this cell for removal by setting the low bit.
+                ASSERT(!(reinterpret_cast<intptr_t>(node.cell) & CellToSweepTag));
+                node.cell = reinterpret_cast<JSCell*>(reinterpret_cast<intptr_t>(node.cell) | CellToSweepTag);
+                m_hasCellsToSweep = true;
+                return;
+            }
+            if (cell < node.cell)
+                end = middle;
+            else
+                start = middle + 1;
+        }
+    }
+
+    if (m_previous)
+        m_previous->sweepCell(cell);
+}
+
+void HeapSnapshot::shrinkToFit()
+{
+    if (m_finalized && m_hasCellsToSweep) {
+        m_nodes.removeAllMatching(
+            [&] (const HeapSnapshotNode& node) -> bool {
+                return reinterpret_cast<intptr_t>(node.cell) & CellToSweepTag;
+            });
+        m_nodes.shrinkToFit();
+        m_hasCellsToSweep = false;
+    }
+
+    if (m_previous)
+        m_previous->shrinkToFit();
+}
+
+void HeapSnapshot::finalize()
+{
+    ASSERT(!m_finalized);
+    m_finalized = true;
+
+    // Nodes are appended to the snapshot in identifier order.
+    // Now that we have the complete list of nodes we will sort
+    // them in a different order. Remember the range of identifiers
+    // in this snapshot.
+    if (!isEmpty()) {
+        m_firstObjectIdentifier = m_nodes.first().identifier;
+        m_lastObjectIdentifier = m_nodes.last().identifier;
+    }
+
+    std::sort(m_nodes.begin(), m_nodes.end(), [] (const HeapSnapshotNode& a, const HeapSnapshotNode& b) {
+        return a.cell < b.cell;
+    });
+
+#ifndef NDEBUG
+    // Assert there are no duplicates or nullptr cells.
+    JSCell* previousCell = nullptr;
+    for (auto& node : m_nodes) {
+        ASSERT(node.cell);
+        ASSERT(!(reinterpret_cast<intptr_t>(node.cell) & CellToSweepTag));
+        if (previousCell)
+            ASSERT(node.cell != previousCell);
+        previousCell = node.cell;
+    }
+#endif
+}
+
+Optional<HeapSnapshotNode> HeapSnapshot::nodeForCell(JSCell* cell)
+{
+    ASSERT(m_finalized);
+
+    if (!isEmpty()) {
+        unsigned start = 0;
+        unsigned end = m_nodes.size();
+        while (start != end) {
+            unsigned middle = start + ((end - start) / 2);
+            HeapSnapshotNode& node = m_nodes[middle];
+            if (cell == node.cell)
+                return Optional<HeapSnapshotNode>(node);
+            if (cell < node.cell)
+                end = middle;
+            else
+                start = middle + 1;
+        }
+    }
+
+    if (m_previous)
+        return m_previous->nodeForCell(cell);
+
+    return Nullopt;
+}
+
+Optional<HeapSnapshotNode> HeapSnapshot::nodeForObjectIdentifier(unsigned objectIdentifier)
+{
+    if (isEmpty()) {
+        if (m_previous)
+            return m_previous->nodeForObjectIdentifier(objectIdentifier);
+        return Nullopt;
+    }
+
+    if (objectIdentifier > m_lastObjectIdentifier)
+        return Nullopt;
+
+    if (objectIdentifier < m_firstObjectIdentifier) {
+        if (m_previous)
+            return m_previous->nodeForObjectIdentifier(objectIdentifier);
+        return Nullopt;
+    }
+
+    for (auto& node : m_nodes) {
+        if (node.identifier == objectIdentifier)
+            return Optional<HeapSnapshotNode>(node);
+    }
+
+    return Nullopt;
+}
+
+} // namespace JSC

Added: trunk/Source/_javascript_Core/heap/HeapSnapshot.h (0 => 197489)


--- trunk/Source/_javascript_Core/heap/HeapSnapshot.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshot.h	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef HeapSnapshot_h
+#define HeapSnapshot_h
+
+#include "HeapSnapshotBuilder.h"
+#include <wtf/Optional.h>
+
+namespace JSC {
+
+class HeapSnapshot {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    HeapSnapshot(HeapSnapshot*);
+    ~HeapSnapshot();
+
+    HeapSnapshot* previous() const { return m_previous; }
+
+    void appendNode(const HeapSnapshotNode&);
+    void sweepCell(JSCell*);
+    void shrinkToFit();
+    void finalize();
+
+    bool isEmpty() const { return m_nodes.isEmpty(); }
+    Optional<HeapSnapshotNode> nodeForCell(JSCell*);
+    Optional<HeapSnapshotNode> nodeForObjectIdentifier(unsigned objectIdentifier);
+
+private:
+    friend class HeapSnapshotBuilder;
+    static const intptr_t CellToSweepTag = 1;
+
+    Vector<HeapSnapshotNode> m_nodes;
+    HeapSnapshot* m_previous { nullptr };
+    unsigned m_firstObjectIdentifier { 0 };
+    unsigned m_lastObjectIdentifier { 0 };
+    bool m_finalized { false };
+    bool m_hasCellsToSweep { false };
+};
+
+} // namespace JSC
+
+#endif // HeapSnapshot_h 

Added: trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp (0 => 197489)


--- trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "HeapSnapshotBuilder.h"
+
+#include "DeferGC.h"
+#include "Heap.h"
+#include "HeapProfiler.h"
+#include "HeapSnapshot.h"
+#include "JSCInlines.h"
+#include "JSCell.h"
+#include "VM.h"
+#include <wtf/text/StringBuilder.h>
+
+namespace JSC {
+    
+unsigned HeapSnapshotBuilder::nextAvailableObjectIdentifier = 1;
+unsigned HeapSnapshotBuilder::getNextObjectIdentifier() { return nextAvailableObjectIdentifier++; }
+
+HeapSnapshotBuilder::HeapSnapshotBuilder(HeapProfiler& profiler)
+    : m_profiler(profiler)
+{
+}
+
+HeapSnapshotBuilder::~HeapSnapshotBuilder()
+{
+}
+
+void HeapSnapshotBuilder::buildSnapshot()
+{
+    m_snapshot = std::make_unique<HeapSnapshot>(m_profiler.mostRecentSnapshot());
+    {
+        m_profiler.setActiveSnapshotBuilder(this);
+        m_profiler.vm().heap.collectAllGarbage();
+        m_profiler.setActiveSnapshotBuilder(nullptr);
+    }
+    m_snapshot->finalize();
+
+    m_profiler.appendSnapshot(WTFMove(m_snapshot));
+}
+
+void HeapSnapshotBuilder::appendNode(JSCell* cell)
+{
+    ASSERT(m_profiler.activeSnapshotBuilder() == this);
+    ASSERT(Heap::isMarked(cell));
+
+    if (hasExistingNodeForCell(cell))
+        return;
+
+    std::lock_guard<Lock> lock(m_appendingNodeMutex);
+
+    m_snapshot->appendNode(HeapSnapshotNode(cell, getNextObjectIdentifier()));
+}
+
+void HeapSnapshotBuilder::appendEdge(JSCell* from, JSCell* to)
+{
+    ASSERT(m_profiler.activeSnapshotBuilder() == this);
+    ASSERT(to);
+
+    // Avoid trivial edges.
+    if (from == to)
+        return;
+
+    std::lock_guard<Lock> lock(m_appendingEdgeMutex);
+
+    m_edges.append(HeapSnapshotEdge(from, to));
+}
+
+bool HeapSnapshotBuilder::hasExistingNodeForCell(JSCell* cell)
+{
+    if (!m_snapshot->previous())
+        return false;
+
+    return !!m_snapshot->previous()->nodeForCell(cell);
+}
+
+
+// Heap Snapshot JSON Format:
+//
+//   {
+//      "version": 1.0,
+//      "nodes": [
+//          [<nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <optionalInternal>], ...
+//      ],
+//      "nodeClassNames": [
+//          "string", "Structure", "Object", ...
+//      ],
+//      "edges": [
+//          [<fromNodeId>, <toNodeId>, <edgeTypeIndex>, <optionalEdgeExtraData>], ...
+//      ],
+//      "edgeTypes": [
+//          "Internal", "Property", "Index", "Variable"
+//      ]
+//   }
+//
+// FIXME: Possible compaction improvements:
+//   - eliminate inner array groups and just have a single list with fixed group sizes (meta data section).
+//   - eliminate duplicate edge extra data strings, have an index into a de-duplicated like edgeTypes / nodeClassNames.
+
+static uint8_t edgeTypeToNumber(EdgeType type)
+{
+    return static_cast<uint8_t>(type);
+}
+
+static const char* edgeTypeToString(EdgeType type)
+{
+    switch (type) {
+    case EdgeType::Internal:
+        return "Internal";
+    case EdgeType::Property:
+        return "Property";
+    case EdgeType::Index:
+        return "Index";
+    case EdgeType::Variable:
+        return "Variable";
+    }
+    ASSERT_NOT_REACHED();
+    return "Internal";
+}
+
+String HeapSnapshotBuilder::json()
+{
+    return json([] (const HeapSnapshotNode&) { return true; });
+}
+
+String HeapSnapshotBuilder::json(std::function<bool (const HeapSnapshotNode&)> allowNodeCallback)
+{
+    VM& vm = m_profiler.vm();
+    DeferGCForAWhile deferGC(vm.heap);
+
+    // Build a node to identifier map of allowed nodes to use when serializing edges.
+    HashMap<JSCell*, unsigned> allowedNodeIdentifiers;
+
+    // Build a list of used class names.
+    HashMap<const char*, unsigned> classNameIndexes;
+    unsigned nextClassNameIndex = 0;
+
+    StringBuilder json;
+
+    auto appendNodeJSON = [&] (const HeapSnapshotNode& node) {
+        // Let the client decide if they want to allow or disallow certain nodes.
+        if (!allowNodeCallback(node))
+            return;
+
+        allowedNodeIdentifiers.set(node.cell, node.identifier);
+
+        auto result = classNameIndexes.add(node.cell->classInfo()->className, nextClassNameIndex);
+        if (result.isNewEntry)
+            nextClassNameIndex++;
+        unsigned classNameIndex = result.iterator->value;
+
+        bool isInternal = false;
+        if (!node.cell->isString()) {
+            Structure* structure = node.cell->structure(vm);
+            isInternal = !structure || !structure->globalObject();
+        }
+
+        // [<nodeId>, <sizeInBytes>, <className>, <optionalInternalBoolean>]
+        json.append(',');
+        json.append('[');
+        json.appendNumber(node.identifier);
+        json.append(',');
+        json.appendNumber(node.cell->estimatedSizeInBytes());
+        json.append(',');
+        json.appendNumber(classNameIndex);
+        if (isInternal)
+            json.appendLiteral(",1");
+        json.append(']');
+    };
+
+    bool firstEdge = true;
+    auto appendEdgeJSON = [&] (const HeapSnapshotEdge& edge) {
+        // If the from cell is null, this means a root edge.
+        unsigned fromIdentifier = 0;
+        if (edge.from) {
+            auto fromLookup = allowedNodeIdentifiers.find(edge.from);
+            if (fromLookup == allowedNodeIdentifiers.end())
+                return;
+            fromIdentifier = fromLookup->value;
+        }
+
+        unsigned toIdentifier = 0;
+        if (edge.to) {
+            auto toLookup = allowedNodeIdentifiers.find(edge.to);
+            if (toLookup == allowedNodeIdentifiers.end())
+                return;
+            toIdentifier = toLookup->value;
+        }
+
+        if (!firstEdge)
+            json.append(',');
+        firstEdge = false;
+
+        // [<fromNodeId>, <toNodeId>, <edgeTypeIndex>, <optionalEdgeExtraData>],
+        json.append('[');
+        json.appendNumber(fromIdentifier);
+        json.append(',');
+        json.appendNumber(toIdentifier);
+        json.append(',');
+        json.appendNumber(edgeTypeToNumber(edge.type));
+        json.append(']');
+    };
+
+    json.append('{');
+
+    // version
+    json.appendLiteral("\"version\":1");
+
+    // nodes
+    json.append(',');
+    json.appendLiteral("\"nodes\":");
+    json.append('[');
+    json.appendLiteral("[0,0,\"<root>\"]");
+    for (HeapSnapshot* snapshot = m_profiler.mostRecentSnapshot(); snapshot; snapshot = snapshot->previous()) {
+        for (auto& node : snapshot->m_nodes)
+            appendNodeJSON(node);
+    }
+    json.append(']');
+
+    // node class names
+    json.append(',');
+    json.appendLiteral("\"nodeClassNames\":");
+    json.append('[');
+    Vector<const char *> orderedClassNames(classNameIndexes.size());
+    for (auto& entry : classNameIndexes)
+        orderedClassNames[entry.value] = entry.key;
+    bool firstClassName = true;
+    for (auto& className : orderedClassNames) {
+        if (!firstClassName)
+            json.append(',');
+        firstClassName = false;
+        json.appendQuotedJSONString(className);
+    }
+    json.append(']');
+
+    // edges
+    json.append(',');
+    json.appendLiteral("\"edges\":");
+    json.append('[');
+    for (auto& edge : m_edges)
+        appendEdgeJSON(edge);
+    json.append(']');
+
+    // edge types
+    json.append(',');
+    json.appendLiteral("\"edgeTypes\":");
+    json.append('[');
+    json.appendQuotedJSONString(edgeTypeToString(EdgeType::Internal));
+    json.append(',');
+    json.appendQuotedJSONString(edgeTypeToString(EdgeType::Property));
+    json.append(',');
+    json.appendQuotedJSONString(edgeTypeToString(EdgeType::Index));
+    json.append(',');
+    json.appendQuotedJSONString(edgeTypeToString(EdgeType::Variable));
+    json.append(']');
+
+    json.append('}');
+    return json.toString();
+}
+
+} // namespace JSC

Added: trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h (0 => 197489)


--- trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef HeapSnapshotBuilder_h
+#define HeapSnapshotBuilder_h
+
+#include <functional>
+#include <wtf/Lock.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+class HeapProfiler;
+class HeapSnapshot;
+class JSCell;
+
+struct HeapSnapshotNode {
+    HeapSnapshotNode(JSCell* cell, unsigned identifier)
+        : cell(cell)
+        , identifier(identifier)
+    { }
+
+    JSCell* cell;
+    unsigned identifier;
+};
+
+enum class EdgeType : uint8_t {
+    Internal,     // Normal strong reference. No name.
+    Property,     // Named property. In `object.property` the name is "property"
+    Index,        // Indexed property. In `array[0]` name is index "0".
+    Variable,     // Variable held by a scope. In `let x, f=() => x++` name is "x" in f's captured scope.
+    // FIXME: <https://webkit.org/b/154934> Heap Snapshot should include "Weak" edges
+};
+
+struct HeapSnapshotEdge {
+    HeapSnapshotEdge(JSCell* from, JSCell* to)
+        : from(from)
+        , to(to)
+        , type(EdgeType::Internal)
+    { }
+
+    JSCell* from;
+    JSCell* to;
+    EdgeType type;
+};
+
+class JS_EXPORT_PRIVATE HeapSnapshotBuilder {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    HeapSnapshotBuilder(HeapProfiler&);
+    ~HeapSnapshotBuilder();
+
+    static unsigned nextAvailableObjectIdentifier;
+    static unsigned getNextObjectIdentifier();    
+
+    // Performs a garbage collection that builds a snapshot of all live cells.
+    void buildSnapshot();
+
+    // A marked cell.
+    void appendNode(JSCell*);
+
+    // A reference from one cell to another.
+    void appendEdge(JSCell* from, JSCell* to);
+
+    String json();
+    String json(std::function<bool (const HeapSnapshotNode&)> allowNodeCallback);
+
+private:
+    // Finalized snapshots are not modified during building. So searching them
+    // for an existing node can be done concurrently without a lock.
+    bool hasExistingNodeForCell(JSCell*);
+
+    HeapProfiler& m_profiler;
+
+    // SlotVisitors run in parallel.
+    Lock m_appendingNodeMutex;
+    std::unique_ptr<HeapSnapshot> m_snapshot;
+    Lock m_appendingEdgeMutex;
+    Vector<HeapSnapshotEdge> m_edges;
+};
+
+} // namespace JSC
+
+#endif // HeapSnapshotBuilder_h 

Modified: trunk/Source/_javascript_Core/heap/SlotVisitor.cpp (197488 => 197489)


--- trunk/Source/_javascript_Core/heap/SlotVisitor.cpp	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/heap/SlotVisitor.cpp	2016-03-03 05:15:56 UTC (rev 197489)
@@ -31,6 +31,8 @@
 #include "CopiedBlockInlines.h"
 #include "CopiedSpace.h"
 #include "CopiedSpaceInlines.h"
+#include "HeapProfiler.h"
+#include "HeapSnapshotBuilder.h"
 #include "JSArray.h"
 #include "JSDestructibleObject.h"
 #include "VM.h"
@@ -94,6 +96,9 @@
 {
     if (heap()->operationInProgress() == FullCollection)
         ASSERT(m_opaqueRoots.isEmpty()); // Should have merged by now.
+
+    if (HeapProfiler* heapProfiler = vm().heapProfiler())
+        m_heapSnapshotBuilder = heapProfiler->activeSnapshotBuilder();
 }
 
 void SlotVisitor::reset()
@@ -101,6 +106,8 @@
     m_bytesVisited = 0;
     m_bytesCopied = 0;
     m_visitCount = 0;
+    m_heapSnapshotBuilder = nullptr;
+    ASSERT(!m_currentCell);
     ASSERT(m_stack.isEmpty());
 }
 
@@ -121,6 +128,10 @@
 {
     if (!value || !value.isCell())
         return;
+
+    if (m_heapSnapshotBuilder)
+        m_heapSnapshotBuilder->appendEdge(m_currentCell, value.asCell());
+
     setMarkedAndAppendToMarkStack(value.asCell());
 }
 
@@ -155,12 +166,37 @@
     m_visitCount++;
     m_bytesVisited += MarkedBlock::blockFor(cell)->cellSize();
     m_stack.append(cell);
+
+    if (m_heapSnapshotBuilder)
+        m_heapSnapshotBuilder->appendNode(cell);
 }
 
+class SetCurrentCellScope {
+public:
+    SetCurrentCellScope(SlotVisitor& visitor, const JSCell* cell)
+        : m_visitor(visitor)
+    {
+        ASSERT(!m_visitor.m_currentCell);
+        m_visitor.m_currentCell = const_cast<JSCell*>(cell);
+    }
+
+    ~SetCurrentCellScope()
+    {
+        ASSERT(m_visitor.m_currentCell);
+        m_visitor.m_currentCell = nullptr;
+    }
+
+private:
+    SlotVisitor& m_visitor;
+};
+
+
 ALWAYS_INLINE void SlotVisitor::visitChildren(const JSCell* cell)
 {
     ASSERT(Heap::isMarked(cell));
 
+    SetCurrentCellScope currentCellScope(*this, cell);
+
     m_currentObjectCellStateBeforeVisiting = cell->cellState();
     cell->setCellState(CellState::OldBlack);
     

Modified: trunk/Source/_javascript_Core/heap/SlotVisitor.h (197488 => 197489)


--- trunk/Source/_javascript_Core/heap/SlotVisitor.h	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/heap/SlotVisitor.h	2016-03-03 05:15:56 UTC (rev 197489)
@@ -37,6 +37,7 @@
 class ConservativeRoots;
 class GCThreadSharedData;
 class Heap;
+class HeapSnapshotBuilder;
 template<typename T> class JITWriteBarrier;
 class UnconditionalFinalizer;
 template<typename T> class Weak;
@@ -47,6 +48,7 @@
     WTF_MAKE_NONCOPYABLE(SlotVisitor);
     WTF_MAKE_FAST_ALLOCATED;
 
+    friend class SetCurrentCellScope;
     friend class HeapRootVisitor; // Allowed to mark a JSValue* or JSCell** directly.
     friend class Heap;
 
@@ -111,6 +113,8 @@
 
     void dump(PrintStream&) const;
 
+    bool isBuildingHeapSnapshot() const { return !!m_heapSnapshotBuilder; }
+
 private:
     friend class ParallelModeEnabler;
     
@@ -137,6 +141,9 @@
     
     Heap& m_heap;
 
+    HeapSnapshotBuilder* m_heapSnapshotBuilder { nullptr };
+    JSCell* m_currentCell { nullptr };
+
     CellState m_currentObjectCellStateBeforeVisiting { CellState::NewWhite };
 
 public:

Modified: trunk/Source/_javascript_Core/jsc.cpp (197488 => 197489)


--- trunk/Source/_javascript_Core/jsc.cpp	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/jsc.cpp	2016-03-03 05:15:56 UTC (rev 197489)
@@ -32,6 +32,8 @@
 #include "Disassembler.h"
 #include "Exception.h"
 #include "ExceptionHelpers.h"
+#include "HeapProfiler.h"
+#include "HeapSnapshotBuilder.h"
 #include "HeapStatistics.h"
 #include "InitializeThreading.h"
 #include "Interpreter.h"
@@ -465,12 +467,67 @@
     Vector<int> m_vector;
 };
 
+class SimpleObject : public JSNonFinalObject {
+public:
+    SimpleObject(VM& vm, Structure* structure)
+        : Base(vm, structure)
+    {
+    }
+
+    typedef JSNonFinalObject Base;
+    static const bool needsDestruction = false;
+
+    static SimpleObject* create(VM& vm, JSGlobalObject* globalObject)
+    {
+        Structure* structure = createStructure(vm, globalObject, jsNull());
+        SimpleObject* simpleObject = new (NotNull, allocateCell<SimpleObject>(vm.heap, sizeof(SimpleObject))) SimpleObject(vm, structure);
+        simpleObject->finishCreation(vm);
+        return simpleObject;
+    }
+
+    void finishCreation(VM& vm)
+    {
+        Base::finishCreation(vm);
+    }
+
+    static void visitChildren(JSCell* cell, SlotVisitor& visitor)
+    {
+        SimpleObject* thisObject = jsCast<SimpleObject*>(cell);
+        ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+        Base::visitChildren(thisObject, visitor);
+        visitor.append(&thisObject->m_hiddenValue);
+    }
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+    {
+        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+    }
+
+    JSValue hiddenValue()
+    {
+        return m_hiddenValue.get();
+    }
+
+    void setHiddenValue(VM& vm, JSValue value)
+    {
+        ASSERT(value.isCell());
+        m_hiddenValue.set(vm, this, value);
+    }
+
+    DECLARE_INFO;
+
+private:
+    WriteBarrier<Unknown> m_hiddenValue;
+};
+
+
 const ClassInfo Element::s_info = { "Element", &Base::s_info, 0, CREATE_METHOD_TABLE(Element) };
 const ClassInfo Masquerader::s_info = { "Masquerader", &Base::s_info, 0, CREATE_METHOD_TABLE(Masquerader) };
 const ClassInfo Root::s_info = { "Root", &Base::s_info, 0, CREATE_METHOD_TABLE(Root) };
 const ClassInfo ImpureGetter::s_info = { "ImpureGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(ImpureGetter) };
 const ClassInfo CustomGetter::s_info = { "CustomGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(CustomGetter) };
 const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &Base::s_info, 0, CREATE_METHOD_TABLE(RuntimeArray) };
+const ClassInfo SimpleObject::s_info = { "SimpleObject", &Base::s_info, 0, CREATE_METHOD_TABLE(SimpleObject) };
 
 ElementHandleOwner* Element::handleOwner()
 {
@@ -501,6 +558,9 @@
 static EncodedJSValue JSC_HOST_CALL functionCreateRoot(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCreateElement(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionGetElement(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionCreateSimpleObject(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionGetHiddenValue(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionSetHiddenValue(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
@@ -555,6 +615,7 @@
 static EncodedJSValue JSC_HOST_CALL functionLoadModule(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionPlatformSupportsSamplingProfiler(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshot(ExecState*);
 #if ENABLE(SAMPLING_PROFILER)
 static EncodedJSValue JSC_HOST_CALL functionStartSamplingProfiler(ExecState*);
 static EncodedJSValue JSC_HOST_CALL functionSamplingProfilerStackTraces(ExecState*);
@@ -708,6 +769,10 @@
         addFunction(vm, "getElement", functionGetElement, 1);
         addFunction(vm, "setElementRoot", functionSetElementRoot, 2);
         
+        addConstructableFunction(vm, "SimpleObject", functionCreateSimpleObject, 0);
+        addFunction(vm, "getHiddenValue", functionGetHiddenValue, 1);
+        addFunction(vm, "setHiddenValue", functionSetHiddenValue, 2);
+        
         putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "DFGTrue"), 0, functionFalse1, DFGTrueIntrinsic, DontEnum);
         putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "OSRExit"), 0, functionUndefined1, OSRExitIntrinsic, DontEnum);
         putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "isFinalTier"), 0, functionFalse2, IsFinalTierIntrinsic, DontEnum);
@@ -747,6 +812,7 @@
         addFunction(vm, "checkModuleSyntax", functionCheckModuleSyntax, 1);
 
         addFunction(vm, "platformSupportsSamplingProfiler", functionPlatformSupportsSamplingProfiler, 0);
+        addFunction(vm, "generateHeapSnapshot", functionGenerateHeapSnapshot, 0);
 #if ENABLE(SAMPLING_PROFILER)
         addFunction(vm, "startSamplingProfiler", functionStartSamplingProfiler, 0);
         addFunction(vm, "samplingProfilerStackTraces", functionSamplingProfilerStackTraces, 0);
@@ -1133,6 +1199,28 @@
     return JSValue::encode(jsUndefined());
 }
 
+EncodedJSValue JSC_HOST_CALL functionCreateSimpleObject(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    return JSValue::encode(SimpleObject::create(exec->vm(), exec->lexicalGlobalObject()));
+}
+
+EncodedJSValue JSC_HOST_CALL functionGetHiddenValue(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    SimpleObject* simpleObject = jsCast<SimpleObject*>(exec->argument(0).asCell());
+    return JSValue::encode(simpleObject->hiddenValue());
+}
+
+EncodedJSValue JSC_HOST_CALL functionSetHiddenValue(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+    SimpleObject* simpleObject = jsCast<SimpleObject*>(exec->argument(0).asCell());
+    JSValue value = exec->argument(1);
+    simpleObject->setHiddenValue(exec->vm(), value);
+    return JSValue::encode(jsUndefined());
+}
+
 EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState* exec)
 {
     JSLockHolder lock(exec);
@@ -1646,6 +1734,19 @@
 #endif
 }
 
+EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshot(ExecState* exec)
+{
+    JSLockHolder lock(exec);
+
+    HeapSnapshotBuilder snapshotBuilder(exec->vm().ensureHeapProfiler());
+    snapshotBuilder.buildSnapshot();
+
+    String jsonString = snapshotBuilder.json();
+    EncodedJSValue result = JSValue::encode(JSONParse(exec, jsonString));
+    RELEASE_ASSERT(!exec->hadException());
+    return result;
+}
+
 #if ENABLE(SAMPLING_PROFILER)
 EncodedJSValue JSC_HOST_CALL functionStartSamplingProfiler(ExecState* exec)
 {

Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (197488 => 197489)


--- trunk/Source/_javascript_Core/runtime/VM.cpp	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp	2016-03-03 05:15:56 UTC (rev 197489)
@@ -50,6 +50,7 @@
 #include "GetterSetter.h"
 #include "Heap.h"
 #include "HeapIterationScope.h"
+#include "HeapProfiler.h"
 #include "HostCallReturnValue.h"
 #include "Identifier.h"
 #include "IncrementalSweeper.h"
@@ -443,6 +444,13 @@
     return *m_watchdog;
 }
 
+HeapProfiler& VM::ensureHeapProfiler()
+{
+    if (!m_heapProfiler)
+        m_heapProfiler = std::make_unique<HeapProfiler>(*this);
+    return *m_heapProfiler;
+}
+
 #if ENABLE(SAMPLING_PROFILER)
 void VM::ensureSamplingProfiler(RefPtr<Stopwatch>&& stopwatch)
 {

Modified: trunk/Source/_javascript_Core/runtime/VM.h (197488 => 197489)


--- trunk/Source/_javascript_Core/runtime/VM.h	2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/runtime/VM.h	2016-03-03 05:15:56 UTC (rev 197489)
@@ -87,6 +87,7 @@
 class HandleStack;
 class TypeProfiler;
 class TypeProfilerLog;
+class HeapProfiler;
 class Identifier;
 class Interpreter;
 class JSBoundSlotBaseFunction;
@@ -249,6 +250,9 @@
     JS_EXPORT_PRIVATE Watchdog& ensureWatchdog();
     JS_EXPORT_PRIVATE Watchdog* watchdog() { return m_watchdog.get(); }
 
+    JS_EXPORT_PRIVATE HeapProfiler* heapProfiler() const { return m_heapProfiler.get(); }
+    JS_EXPORT_PRIVATE HeapProfiler& ensureHeapProfiler();
+
 #if ENABLE(SAMPLING_PROFILER)
     JS_EXPORT_PRIVATE SamplingProfiler* samplingProfiler() { return m_samplingProfiler.get(); }
     JS_EXPORT_PRIVATE void ensureSamplingProfiler(RefPtr<Stopwatch>&&);
@@ -672,6 +676,7 @@
     Deque<std::unique_ptr<QueuedTask>> m_microtaskQueue;
     MallocPtr<EncodedJSValue> m_exceptionFuzzBuffer;
     RefPtr<Watchdog> m_watchdog;
+    std::unique_ptr<HeapProfiler> m_heapProfiler;
 #if ENABLE(SAMPLING_PROFILER)
     RefPtr<SamplingProfiler> m_samplingProfiler;
 #endif

Added: trunk/Source/_javascript_Core/tests/heapProfiler/basic-edges.js (0 => 197489)


--- trunk/Source/_javascript_Core/tests/heapProfiler/basic-edges.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/heapProfiler/basic-edges.js	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,58 @@
+load("./driver/driver.js");
+
+function excludeStructure(edges) {
+    return edges.filter((x) => x.to.className !== "Structure");
+}
+
+let simpleObject1NodeId;
+let simpleObject2NodeId;
+
+let simpleObject1 = new SimpleObject;
+let simpleObject2 = new SimpleObject;
+
+(function() {
+    let snapshot = createCheapHeapSnapshot();
+    let nodes = snapshot.nodesWithClassName("SimpleObject");
+    assert(nodes.length === 2, "Snapshot should contain 2 'SimpleObject' instances");
+    let simpleObject1Node = nodes[0].outgoingEdges.length === 2 ? nodes[0] : nodes[1];
+    let simpleObject2Node = nodes[0].outgoingEdges.length === 1 ? nodes[0] : nodes[1];
+    assert(simpleObject1Node.outgoingEdges.length === 1, "'simpleObject1' should reference only its structure");
+    assert(simpleObject2Node.outgoingEdges.length === 1, "'simpleObject2' should reference only its structure");
+})();
+
+setHiddenValue(simpleObject1, simpleObject2);
+
+(function() {
+    let snapshot = createCheapHeapSnapshot();
+    let nodes = snapshot.nodesWithClassName("SimpleObject");
+    assert(nodes.length === 2, "Snapshot should contain 2 'SimpleObject' instances");
+    let simpleObject1Node = nodes[0].outgoingEdges.length === 2 ? nodes[0] : nodes[1];
+    let simpleObject2Node = nodes[0].outgoingEdges.length === 1 ? nodes[0] : nodes[1];
+    assert(simpleObject1Node.outgoingEdges.length === 2, "'simpleObject1' should reference its structure and hidden value");
+    assert(simpleObject2Node.outgoingEdges.length === 1, "'simpleObject2' should reference only its structure");
+    assert(excludeStructure(simpleObject1Node.outgoingEdges)[0].to.id === simpleObject2Node.id, "'simpleObject1' should reference 'simpleObject2'");
+    simpleObject1NodeId = simpleObject1Node.id;
+    simpleObject2NodeId = simpleObject2Node.id;
+})();
+
+simpleObject2 = null;
+
+(function() {
+    let snapshot = createCheapHeapSnapshot();
+    let nodes = snapshot.nodesWithClassName("SimpleObject");
+    assert(nodes.length === 2, "Snapshot should contain 2 'SimpleObject' instances");
+    let simpleObject1Node = nodes[0].id === simpleObject1NodeId ? nodes[0] : nodes[1];
+    let simpleObject2Node = nodes[0].id === simpleObject2NodeId ? nodes[0] : nodes[1];
+    assert(simpleObject1Node.id === simpleObject1NodeId && simpleObject2Node.id === simpleObject2NodeId, "node identifiers were maintained");
+    assert(simpleObject1Node.outgoingEdges.length === 2, "'simpleObject1' should reference its structure and hidden value");
+    assert(simpleObject2Node.outgoingEdges.length === 1, "'simpleObject2' should reference only its structure");
+    assert(excludeStructure(simpleObject1Node.outgoingEdges)[0].to.id === simpleObject2NodeId, "'simpleObject1' should reference 'simpleObject2'");
+})();
+
+simpleObject1 = null;
+
+(function() {
+    let snapshot = createCheapHeapSnapshot();
+    let nodes = snapshot.nodesWithClassName("SimpleObject");
+    assert(nodes.length === 0, "Snapshot should not contain a 'SimpleObject' instance");
+})();

Added: trunk/Source/_javascript_Core/tests/heapProfiler/basic-nodes.js (0 => 197489)


--- trunk/Source/_javascript_Core/tests/heapProfiler/basic-nodes.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/heapProfiler/basic-nodes.js	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,78 @@
+load("./driver/driver.js");
+
+function hasDifferentSizeNodes(nodes) {
+    let seenSize = nodes[0].size;
+    for (let node of nodes) {
+        if (node.size !== seenSize)
+            return true;
+    }
+    return false;
+}
+
+function hasAllInternalNodes(nodes) {
+    for (let node of nodes) {
+        if (!node.internal)
+            return false;
+    }
+    return true;
+}
+
+function sorted(nodes) {
+    return nodes.sort((a, b) => a.id - b.id);
+}
+
+let simpleObject1NodeId;
+let simpleObject2NodeId;
+
+(function() {
+    let snapshot = createCheapHeapSnapshot();
+    assert(snapshot.nodesWithClassName("global").length === 1, "Snapshot should contain a single 'global' node");
+    assert(snapshot.nodesWithClassName("Structure").length > 0, "Snapshot should contain 'Structure' nodes");
+    assert(snapshot.nodesWithClassName("ThisClassNameDoesNotExist").length === 0, "Snapshot should not contain 'ThisClassNameDoesNotExist' nodes");
+
+    let strings = snapshot.nodesWithClassName("string");
+    assert(strings.length > 0, "Snapshot should contain 'string' nodes");
+    assert(hasDifferentSizeNodes(strings), "'string' nodes should have different sizes");
+
+    let nativeExecutables = snapshot.nodesWithClassName("NativeExecutable");
+    assert(nativeExecutables.length > 0, "Snapshot should contain 'NativeExecutable' nodes");
+    assert(!hasDifferentSizeNodes(nativeExecutables), "'NativeExecutable' nodes should all be the same size");
+    assert(hasAllInternalNodes(nativeExecutables), "'NativeExecutable' nodes should all be internal");
+
+    assert(snapshot.nodesWithClassName("SimpleObject").length === 0, "Snapshot should not contain a 'SimpleObject' instance");
+})();
+
+let simpleObject1 = new SimpleObject;
+
+(function() {
+    let snapshot = createCheapHeapSnapshot();
+    let nodes = sorted(snapshot.nodesWithClassName("SimpleObject"));
+    let [simpleObject1Node] = nodes;
+    simpleObject1NodeId = nodes[0].id;
+    assert(nodes.length === 1, "Snapshot should contain 1 'SimpleObject' instance");
+    assert(simpleObject1Node.outgoingEdges.length === 1, "'simpleObject1' should only reference its structure");
+    assert(simpleObject1Node.outgoingEdges[0].to.className === "Structure", "'simpleObject1' should reference a Structure");
+})();
+
+let simpleObjectList = [];
+for (let i = 0; i < 1234; ++i)
+    simpleObjectList.push(new SimpleObject);
+
+(function() {
+    let snapshot = createCheapHeapSnapshot();
+    let nodes = sorted(snapshot.nodesWithClassName("SimpleObject"));
+    simpleObject1NodeId = nodes[0].id;
+    simpleObject2NodeId = nodes[1].id;
+    assert(nodes.length === 1235, "Snapshot should contain 1235 'SimpleObject' instances");
+    assert(nodes[0].id === simpleObject1NodeId, "'simpleObject1' should maintain the same identifier");
+    assert(simpleObject1NodeId < simpleObject2NodeId, "node identifiers should always increase across snapshots");
+})();
+
+simpleObject1 = null;
+simpleObjectList.fill(null);
+
+(function() {
+    let snapshot = createCheapHeapSnapshot();
+    let nodes = snapshot.nodesWithClassName("SimpleObject");
+    assert(nodes.length === 0, "Snapshot should not contain a 'SimpleObject' instance");
+})();

Added: trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js (0 => 197489)


--- trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,242 @@
+function assert(condition, reason) {
+    if (!condition)
+        throw new Error(reason);
+}
+
+// -----------------
+// CheapHeapSnapshot
+//
+// Contains two large lists of all node data and all edge data.
+// Lazily creates node and edge objects off of indexes into these lists.
+
+// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:internal>, <4:firstEdgeIndex>];
+const nodeFieldCount = 5;
+const nodeIdOffset = 0;
+const nodeSizeOffset = 1;
+const nodeClassNameOffset = 2;
+const nodeInternalOffset = 3;
+const nodeFirstEdgeOffset = 4;
+const nodeNoEdgeValue = 0xffffffff; // UINT_MAX
+
+// [<0:from-id>, <1:to-id>, <2:typeTableIndex>, <3:data>]
+const edgeFieldCount = 4;
+const edgeFromIdOffset = 0;
+const edgeToIdOffset = 1;
+const edgeTypeOffset = 2;
+const edgeDataOffset = 3;
+
+CheapHeapSnapshotNode = class CheapHeapSnapshotNode
+{
+    constructor(snapshot, nodeIndex)
+    {
+        assert((nodeIndex % nodeFieldCount) === 0, "Bad Node Index: " + nodeIndex);
+
+        let nodes = snapshot.nodes;
+        this.id = nodes[nodeIndex + nodeIdOffset];
+        this.size = nodes[nodeIndex + nodeSizeOffset];
+        this.className = snapshot.classNameFromTableIndex(nodes[nodeIndex + nodeClassNameOffset]);
+        this.internal = nodes[nodeIndex + nodeInternalOffset] ? true : false;
+
+        this.outgoingEdges = [];
+        let firstEdgeIndex = nodes[nodeIndex + nodeFirstEdgeOffset];
+        if (firstEdgeIndex !== nodeNoEdgeValue) {
+            for (let i = firstEdgeIndex; i < snapshot.edges.length; i += edgeFieldCount) {
+                if (snapshot.edges[i + edgeFromIdOffset] !== this.id)
+                    break;
+                this.outgoingEdges.push(new CheapHeapSnapshotEdge(snapshot, i));
+            }
+        }
+    }
+}
+
+CheapHeapSnapshotEdge = class CheapHeapSnapshotEdge
+{
+    constructor(snapshot, edgeIndex)
+    {
+        assert((edgeIndex % edgeFieldCount) === 0, "Bad Edge Index: " + edgeIndex);
+        this.snapshot = snapshot;
+
+        let edges = snapshot.edges;
+        this.fromId = edges[edgeIndex + edgeFromIdOffset];
+        this.toId = edges[edgeIndex + edgeToIdOffset];
+        this.type = snapshot.edgeTypeFromTableIndex(edges[edgeIndex + edgeTypeOffset]);
+        this.data = "" + edgeDataOffset];
+    }
+
+    get from() { return this.snapshot.nodeWithIdentifier(this.fromId); }
+    get to() { return this.snapshot.nodeWithIdentifier(this.toId); }
+}
+
+CheapHeapSnapshot = class CheapHeapSnapshot
+{
+    constructor(json)
+    {
+        let {nodes, nodeClassNames, edges, edgeTypes} = json;
+
+        this._nodes = new Uint32Array(nodes.length * nodeFieldCount);
+        this._edges = new Uint32Array(edges.length * edgeFieldCount);
+        this._nodeIdentifierToIndex = new Map; // <id> => index in _nodes
+
+        this._edgeTypesTable = edgeTypes;
+        this._nodeClassNamesTable = nodeClassNames;
+
+        let n = 0;
+        nodes.forEach((nodePayload) => {
+            let [id, size, classNameTableIndex, internal] = nodePayload;
+            this._nodeIdentifierToIndex.set(id, n);
+            this._nodes[n++] = id;
+            this._nodes[n++] = size;
+            this._nodes[n++] = classNameTableIndex;
+            this._nodes[n++] = internal;
+            this._nodes[n++] = nodeNoEdgeValue;
+        });
+
+        let e = 0;
+        let lastNodeIdentifier = -1;
+        edges.sort((a, b) => a[0] - b[0]).forEach((edgePayload) => {
+            let [fromIdentifier, toIdentifier, edgeTypeTableIndex, data] = edgePayload;
+            if (fromIdentifier !== lastNodeIdentifier) {
+                let nodeIndex = this._nodeIdentifierToIndex.get(fromIdentifier);
+                assert(this._nodes[nodeIndex + nodeIdOffset] === fromIdentifier, "Node lookup failed");
+                this._nodes[nodeIndex + nodeFirstEdgeOffset] = e;
+                lastNodeIdentifier = fromIdentifier;
+            }
+            this._edges[e++] = fromIdentifier;
+            this._edges[e++] = toIdentifier;
+            this._edges[e++] = edgeTypeTableIndex;
+            this._edges[e++] = data;
+        });
+    }
+
+    get nodes() { return this._nodes; }
+    get edges() { return this._edges; }
+
+    nodeWithIdentifier(id)
+    {
+        return new CheapHeapSnapshotNode(this, this._nodeIdentifierToIndex.get(id));
+    }
+
+    nodesWithClassName(className)
+    {
+        let result = [];
+        for (let i = 0; i < this._nodes.length; i += nodeFieldCount) {
+            let classNameTableIndex = this._nodes[i + nodeClassNameOffset];
+            if (this.classNameFromTableIndex(classNameTableIndex) === className)
+                result.push(new CheapHeapSnapshotNode(this, i));
+        }
+        return result;
+    }
+
+    classNameFromTableIndex(tableIndex)
+    {
+        return this._nodeClassNamesTable[tableIndex];
+    }
+
+    edgeTypeFromTableIndex(tableIndex)
+    {
+        return this._edgeTypesTable[tableIndex];
+    }
+}
+
+function createCheapHeapSnapshot() {
+    let json = generateHeapSnapshot();
+
+    let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
+    assert(version === 1, "Heap Snapshot payload should be version 1");
+    assert(nodes.length, "Heap Snapshot should have nodes");
+    assert(nodeClassNames.length, "Heap Snapshot should have nodeClassNames");
+    assert(edges.length, "Heap Snapshot should have edges");
+    assert(edgeTypes.length, "Heap Snapshot should have edgeTypes");
+
+    return new CheapHeapSnapshot(json);
+}
+
+
+// ------------
+// HeapSnapshot
+//
+// This creates a lot of objects that make it easy to walk the entire node graph
+// (incoming and outgoing edges). However when a test creates multiple snapshots
+// with snapshots in scope this can quickly explode into a snapshot with a massive
+// number of nodes/edges. For such cases create CheapHeapSnapshots, which create
+// a very small number of objects per Heap Snapshot.
+
+HeapSnapshotNode = class HeapSnapshotNode
+{
+    constructor(id, className, size, internal)
+    {
+        this.id = id;
+        this.className = className;
+        this.size = size; 
+        this.internal = internal;
+        this.incomingEdges = [];
+        this.outgoingEdges = [];
+    }
+}
+
+HeapSnapshotEdge = class HeapSnapshotEdge
+{
+    constructor(from, to, type, data)
+    {
+        this.from = from;
+        this.to = to;
+        this.type = type;
+        this.data = ""
+    }
+}
+
+HeapSnapshot = class HeapSnapshot
+{
+    constructor(json)
+    {        
+        let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
+
+        this.nodeMap = new Map;
+
+        this.nodes = nodes.map((nodePayload) => {
+            let [id, size, classNameIndex, internal] = nodePayload;
+            let node = new HeapSnapshotNode(id, nodeClassNames[classNameIndex], size, internal);
+            this.nodeMap.set(id, node);
+            return node;
+        });
+
+        edges.map((edgePayload) => {
+            let [fromIdentifier, toIdentifier, edgeTypeIndex, data] = edgePayload;
+            let from = this.nodeMap.get(fromIdentifier);
+            let to = this.nodeMap.get(toIdentifier);
+            assert(from, "Missing node for `from` part of edge");
+            assert(to, "Missing node for `to` part of edge");
+            let edge = new HeapSnapshotEdge(from, to, edgeTypes[edgeTypeIndex], data);
+            from.outgoingEdges.push(edge);
+            to.incomingEdges.push(edge);
+        });
+
+        this.rootNode = this.nodeMap.get(0);
+        assert(this.rootNode, "Missing <root> node with identifier 0");
+        assert(this.rootNode.outgoingEdges.length > 0, "<root> should have children");
+        assert(this.rootNode.incomingEdges.length === 0, "<root> should not have back references");
+    }
+
+    nodesWithClassName(className)
+    {
+        let result = [];
+        for (let node of this.nodes) {
+            if (node.className === className)
+                result.push(node);
+        }
+        return result;
+    }
+}
+
+function createHeapSnapshot() {
+    let json = generateHeapSnapshot();
+
+    let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
+    assert(version === 1, "Heap Snapshot payload should be version 1");
+    assert(nodes.length, "Heap Snapshot should have nodes");
+    assert(nodeClassNames.length, "Heap Snapshot should have nodeClassNames");
+    assert(edges.length, "Heap Snapshot should have edges");
+    assert(edgeTypes.length, "Heap Snapshot should have edgeTypes");
+
+    return new HeapSnapshot(json);
+}

Added: trunk/Source/_javascript_Core/tests/heapProfiler.yaml (0 => 197489)


--- trunk/Source/_javascript_Core/tests/heapProfiler.yaml	                        (rev 0)
+++ trunk/Source/_javascript_Core/tests/heapProfiler.yaml	2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,25 @@
+# Copyright (C) 2016 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1.  Redistributions of source code must retain the above copyright
+#     notice, this list of conditions and the following disclaimer. 
+# 2.  Redistributions in binary form must reproduce the above copyright
+#     notice, this list of conditions and the following disclaimer in the
+#     documentation and/or other materials provided with the distribution. 
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+- path: heapProfiler
+  cmd: defaultRun
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to