<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[197489] trunk/Source/JavaScriptCore</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/197489">197489</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2016-03-02 21:15:56 -0800 (Wed, 02 Mar 2016)</dd>
</dl>
<h3>Log Message</h3>
<pre>Add ability to generate a Heap Snapshot
https://bugs.webkit.org/show_bug.cgi?id=154847
Patch by Joseph Pecoraro <pecoraro@apple.com> 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:
* JavaScriptCore.xcodeproj/project.pbxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.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.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreCMakeListstxt">trunk/Source/JavaScriptCore/CMakeLists.txt</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxproj">trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxprojfilters">trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj">trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapcpp">trunk/Source/JavaScriptCore/heap/Heap.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeaph">trunk/Source/JavaScriptCore/heap/Heap.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapSlotVisitorcpp">trunk/Source/JavaScriptCore/heap/SlotVisitor.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapSlotVisitorh">trunk/Source/JavaScriptCore/heap/SlotVisitor.h</a></li>
<li><a href="#trunkSourceJavaScriptCorejsccpp">trunk/Source/JavaScriptCore/jsc.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeVMcpp">trunk/Source/JavaScriptCore/runtime/VM.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeVMh">trunk/Source/JavaScriptCore/runtime/VM.h</a></li>
</ul>
<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreheapHeapProfilercpp">trunk/Source/JavaScriptCore/heap/HeapProfiler.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapProfilerh">trunk/Source/JavaScriptCore/heap/HeapProfiler.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapSnapshotcpp">trunk/Source/JavaScriptCore/heap/HeapSnapshot.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapSnapshoth">trunk/Source/JavaScriptCore/heap/HeapSnapshot.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapSnapshotBuildercpp">trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapSnapshotBuilderh">trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.h</a></li>
<li>trunk/Source/JavaScriptCore/tests/heapProfiler/</li>
<li><a href="#trunkSourceJavaScriptCoretestsheapProfilerbasicedgesjs">trunk/Source/JavaScriptCore/tests/heapProfiler/basic-edges.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsheapProfilerbasicnodesjs">trunk/Source/JavaScriptCore/tests/heapProfiler/basic-nodes.js</a></li>
<li>trunk/Source/JavaScriptCore/tests/heapProfiler/driver/</li>
<li><a href="#trunkSourceJavaScriptCoretestsheapProfilerdriverdriverjs">trunk/Source/JavaScriptCore/tests/heapProfiler/driver/driver.js</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsheapProfileryaml">trunk/Source/JavaScriptCore/tests/heapProfiler.yaml</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreCMakeListstxt"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/CMakeLists.txt (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/CMakeLists.txt        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/CMakeLists.txt        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -443,6 +443,9 @@
</span><span class="cx"> heap/HandleStack.cpp
</span><span class="cx"> heap/Heap.cpp
</span><span class="cx"> heap/HeapHelperPool.cpp
</span><ins>+ heap/HeapProfiler.cpp
+ heap/HeapSnapshot.cpp
+ heap/HeapSnapshotBuilder.cpp
</ins><span class="cx"> heap/HeapStatistics.cpp
</span><span class="cx"> heap/HeapTimer.cpp
</span><span class="cx"> heap/HeapVerifier.cpp
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -1,3 +1,196 @@
</span><ins>+2016-03-02 Joseph Pecoraro <pecoraro@apple.com>
+
+ 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:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.vcxproj/JavaScriptCore.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.
+
</ins><span class="cx"> 2016-03-02 Filip Pizlo <fpizlo@apple.com>
</span><span class="cx">
</span><span class="cx"> RegExpPrototype should check for exceptions after calling toString and doing so should not be expensive
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -575,6 +575,9 @@
</span><span class="cx"> <ClCompile Include="..\heap\HandleStack.cpp" />
</span><span class="cx"> <ClCompile Include="..\heap\Heap.cpp" />
</span><span class="cx"> <ClCompile Include="..\heap\HeapHelperPool.cpp" />
</span><ins>+ <ClCompile Include="..\heap\HeapProfiler.cpp" />
+ <ClCompile Include="..\heap\HeapSnapshot.cpp" />
+ <ClCompile Include="..\heap\HeapSnapshotBuilder.cpp" />
</ins><span class="cx"> <ClCompile Include="..\heap\HeapStatistics.cpp" />
</span><span class="cx"> <ClCompile Include="..\heap\HeapTimer.cpp" />
</span><span class="cx"> <ClCompile Include="..\heap\HeapVerifier.cpp" />
</span><span class="lines">@@ -1386,6 +1389,9 @@
</span><span class="cx"> <ClInclude Include="..\heap\HeapInlines.h" />
</span><span class="cx"> <ClInclude Include="..\heap\HeapOperation.h" />
</span><span class="cx"> <ClInclude Include="..\heap\HeapRootVisitor.h" />
</span><ins>+ <ClInclude Include="..\heap\HeapProfiler.h" />
+ <ClInclude Include="..\heap\HeapSnapshot.h" />
+ <ClInclude Include="..\heap\HeapSnapshotBuilder.h" />
</ins><span class="cx"> <ClInclude Include="..\heap\HeapStatistics.h" />
</span><span class="cx"> <ClInclude Include="..\heap\HeapTimer.h" />
</span><span class="cx"> <ClInclude Include="..\heap\HeapVerifier.h" />
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorevcxprojJavaScriptCorevcxprojfilters"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -294,6 +294,15 @@
</span><span class="cx"> <ClCompile Include="..\heap\HeapHelperPool.cpp">
</span><span class="cx"> <Filter>heap</Filter>
</span><span class="cx"> </ClCompile>
</span><ins>+ <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>
</ins><span class="cx"> <ClCompile Include="..\heap\HeapStatistics.cpp">
</span><span class="cx"> <Filter>heap</Filter>
</span><span class="cx"> </ClCompile>
</span><span class="lines">@@ -2368,6 +2377,15 @@
</span><span class="cx"> <ClInclude Include="..\heap\HeapRootVisitor.h">
</span><span class="cx"> <Filter>heap</Filter>
</span><span class="cx"> </ClInclude>
</span><ins>+ <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>
</ins><span class="cx"> <ClInclude Include="..\heap\HeapStatistics.h">
</span><span class="cx"> <Filter>heap</Filter>
</span><span class="cx"> </ClInclude>
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -1548,6 +1548,8 @@
</span><span class="cx">                 A513E5CB185F9624007E95AD /* InjectedScriptManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A513E5C9185F9624007E95AD /* InjectedScriptManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 A514B2C2185A684400F3C7CB /* InjectedScriptBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A514B2C0185A684400F3C7CB /* InjectedScriptBase.cpp */; };
</span><span class="cx">                 A514B2C3185A684400F3C7CB /* InjectedScriptBase.h in Headers */ = {isa = PBXBuildFile; fileRef = A514B2C1185A684400F3C7CB /* InjectedScriptBase.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                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 */; };
</ins><span class="cx">                 A532438718568335002ED692 /* InspectorBackendDispatchers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A532438118568317002ED692 /* InspectorBackendDispatchers.cpp */; };
</span><span class="cx">                 A532438818568335002ED692 /* InspectorBackendDispatchers.h in Headers */ = {isa = PBXBuildFile; fileRef = A532438218568317002ED692 /* InspectorBackendDispatchers.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 A532438918568335002ED692 /* InspectorFrontendDispatchers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A532438318568317002ED692 /* InspectorFrontendDispatchers.cpp */; };
</span><span class="lines">@@ -1558,6 +1560,8 @@
</span><span class="cx">                 A5339EC61BB399A60054F005 /* InspectorHeapAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A5339EC51BB399900054F005 /* InspectorHeapAgent.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 A5339EC71BB399A90054F005 /* InspectorHeapAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5339EC41BB399900054F005 /* InspectorHeapAgent.cpp */; };
</span><span class="cx">                 A5339EC91BB4B4600054F005 /* HeapObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = A5339EC81BB4B4510054F005 /* HeapObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                A5398FAB1C750DA40060A963 /* HeapProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = A5398FAA1C750D950060A963 /* HeapProfiler.h */; };
+                A5398FAC1C750DA60060A963 /* HeapProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5398FA91C750D950060A963 /* HeapProfiler.cpp */; };
</ins><span class="cx">                 A53CE08518BC1A5600BEDF76 /* ConsolePrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A53CE08118BC1A5600BEDF76 /* ConsolePrototype.cpp */; };
</span><span class="cx">                 A53CE08618BC1A5600BEDF76 /* ConsolePrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = A53CE08218BC1A5600BEDF76 /* ConsolePrototype.h */; };
</span><span class="cx">                 A53CE08718BC1A5600BEDF76 /* JSConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A53CE08318BC1A5600BEDF76 /* JSConsole.cpp */; };
</span><span class="lines">@@ -1566,6 +1570,8 @@
</span><span class="cx">                 A53F1AC018C90F8F0072EB6D /* framework.sb in Resources */ = {isa = PBXBuildFile; fileRef = A53F1ABE18C90EC70072EB6D /* framework.sb */; };
</span><span class="cx">                 A54982031891D0B00081E5B8 /* EventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54982011891D0B00081E5B8 /* EventLoop.cpp */; };
</span><span class="cx">                 A54982041891D0B00081E5B8 /* EventLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = A54982021891D0B00081E5B8 /* EventLoop.h */; };
</span><ins>+                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, ); }; };
</ins><span class="cx">                 A54CF2F5184EAB2400237F19 /* ScriptValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54CF2F2184EAB2400237F19 /* ScriptValue.cpp */; };
</span><span class="cx">                 A54CF2F6184EAB2400237F19 /* ScriptValue.h in Headers */ = {isa = PBXBuildFile; fileRef = A54CF2F3184EAB2400237F19 /* ScriptValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 A54CF2F9184EAEDA00237F19 /* ScriptObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54CF2F7184EAEDA00237F19 /* ScriptObject.cpp */; };
</span><span class="lines">@@ -3743,6 +3749,8 @@
</span><span class="cx">                 A513E5C9185F9624007E95AD /* InjectedScriptManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptManager.h; sourceTree = "<group>"; };
</span><span class="cx">                 A514B2C0185A684400F3C7CB /* InjectedScriptBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InjectedScriptBase.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 A514B2C1185A684400F3C7CB /* InjectedScriptBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptBase.h; sourceTree = "<group>"; };
</span><ins>+                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>"; };
</ins><span class="cx">                 A532438118568317002ED692 /* InspectorBackendDispatchers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorBackendDispatchers.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 A532438218568317002ED692 /* InspectorBackendDispatchers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InspectorBackendDispatchers.h; sourceTree = "<group>"; };
</span><span class="cx">                 A532438318568317002ED692 /* InspectorFrontendDispatchers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorFrontendDispatchers.cpp; sourceTree = "<group>"; };
</span><span class="lines">@@ -3755,6 +3763,8 @@
</span><span class="cx">                 A5339EC41BB399900054F005 /* InspectorHeapAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorHeapAgent.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 A5339EC51BB399900054F005 /* InspectorHeapAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorHeapAgent.h; sourceTree = "<group>"; };
</span><span class="cx">                 A5339EC81BB4B4510054F005 /* HeapObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapObserver.h; sourceTree = "<group>"; };
</span><ins>+                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>"; };
</ins><span class="cx">                 A53CE08118BC1A5600BEDF76 /* ConsolePrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConsolePrototype.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 A53CE08218BC1A5600BEDF76 /* ConsolePrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConsolePrototype.h; sourceTree = "<group>"; };
</span><span class="cx">                 A53CE08318BC1A5600BEDF76 /* JSConsole.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSConsole.cpp; sourceTree = "<group>"; };
</span><span class="lines">@@ -3763,6 +3773,8 @@
</span><span class="cx">                 A53F1ABE18C90EC70072EB6D /* framework.sb */ = {isa = PBXFileReference; lastKnownFileType = text; path = framework.sb; sourceTree = "<group>"; };
</span><span class="cx">                 A54982011891D0B00081E5B8 /* EventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventLoop.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 A54982021891D0B00081E5B8 /* EventLoop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventLoop.h; sourceTree = "<group>"; };
</span><ins>+                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>"; };
</ins><span class="cx">                 A54CF2F2184EAB2400237F19 /* ScriptValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptValue.cpp; sourceTree = "<group>"; };
</span><span class="cx">                 A54CF2F3184EAB2400237F19 /* ScriptValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptValue.h; sourceTree = "<group>"; };
</span><span class="cx">                 A54CF2F7184EAEDA00237F19 /* ScriptObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptObject.cpp; sourceTree = "<group>"; };
</span><span class="lines">@@ -5093,7 +5105,13 @@
</span><span class="cx">                                 2AD8932917E3868F00668276 /* HeapIterationScope.h */,
</span><span class="cx">                                 A5339EC81BB4B4510054F005 /* HeapObserver.h */,
</span><span class="cx">                                 2A6F462517E959CE00C45C98 /* HeapOperation.h */,
</span><ins>+                                A5398FA91C750D950060A963 /* HeapProfiler.cpp */,
+                                A5398FAA1C750D950060A963 /* HeapProfiler.h */,
</ins><span class="cx">                                 14F97446138C853E00DA1C67 /* HeapRootVisitor.h */,
</span><ins>+                                A54C2AAE1C6544D100A18D78 /* HeapSnapshot.cpp */,
+                                A54C2AAF1C6544D100A18D78 /* HeapSnapshot.h */,
+                                A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */,
+                                A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */,
</ins><span class="cx">                                 C24D31E0161CD695002AA4DB /* HeapStatistics.cpp */,
</span><span class="cx">                                 C24D31E1161CD695002AA4DB /* HeapStatistics.h */,
</span><span class="cx">                                 C2E526BB1590EF000054E48D /* HeapTimer.cpp */,
</span><span class="lines">@@ -6918,6 +6936,7 @@
</span><span class="cx">                                 BC18C3E60E16F5CD00B34460 /* ArrayConstructor.h in Headers */,
</span><span class="cx">                                 996B73171BDA067F00331B84 /* ArrayConstructor.lut.h in Headers */,
</span><span class="cx">                                 0FB7F39515ED8E4600F167B2 /* ArrayConventions.h in Headers */,
</span><ins>+                                A5311C361C77CEC500E6B1B6 /* HeapSnapshotBuilder.h in Headers */,
</ins><span class="cx">                                 A7BDAEC917F4EA1400F6140C /* ArrayIteratorPrototype.h in Headers */,
</span><span class="cx">                                 996B73181BDA068000331B84 /* ArrayIteratorPrototype.lut.h in Headers */,
</span><span class="cx">                                 0F63945515D07057006A597C /* ArrayProfile.h in Headers */,
</span><span class="lines">@@ -7090,6 +7109,7 @@
</span><span class="cx">                                 41359CF30FDD89AD00206180 /* DateConversion.h in Headers */,
</span><span class="cx">                                 BC1166020E1997B4008066DD /* DateInstance.h in Headers */,
</span><span class="cx">                                 14A1563210966365006FA260 /* DateInstanceCache.h in Headers */,
</span><ins>+                                A54C2AB11C6544F200A18D78 /* HeapSnapshot.h in Headers */,
</ins><span class="cx">                                 BCD2034C0E17135E002C7E82 /* DatePrototype.h in Headers */,
</span><span class="cx">                                 BCD203E80E1718F4002C7E82 /* DatePrototype.lut.h in Headers */,
</span><span class="cx">                                 BC18C3FA0E16F5CD00B34460 /* Debugger.h in Headers */,
</span><span class="lines">@@ -7502,6 +7522,7 @@
</span><span class="cx">                                 FE187A0F1C030D6C0038BBCA /* SnippetOperand.h in Headers */,
</span><span class="cx">                                 A1587D701B4DC14100D69849 /* IntlDateTimeFormatConstructor.h in Headers */,
</span><span class="cx">                                 A1587D751B4DC1C600D69849 /* IntlDateTimeFormatConstructor.lut.h in Headers */,
</span><ins>+                                A5398FAB1C750DA40060A963 /* HeapProfiler.h in Headers */,
</ins><span class="cx">                                 A1587D721B4DC14100D69849 /* IntlDateTimeFormatPrototype.h in Headers */,
</span><span class="cx">                                 A1587D761B4DC1C600D69849 /* IntlDateTimeFormatPrototype.lut.h in Headers */,
</span><span class="cx">                                 A1D792FD1B43864B004516F5 /* IntlNumberFormat.h in Headers */,
</span><span class="lines">@@ -8822,6 +8843,7 @@
</span><span class="cx">                                 0FE7211D193B9C590031F6ED /* DFGTransition.cpp in Sources */,
</span><span class="cx">                                 0F63944015C75F1D006A597C /* DFGTypeCheckHoistingPhase.cpp in Sources */,
</span><span class="cx">                                 0FBE0F7616C1DB0F0082C5E8 /* DFGUnificationPhase.cpp in Sources */,
</span><ins>+                                A5398FAC1C750DA60060A963 /* HeapProfiler.cpp in Sources */,
</ins><span class="cx">                                 0F34B14916D42010001CDA5A /* DFGUseKind.cpp in Sources */,
</span><span class="cx">                                 0F3B3A2B15475000003ED0FF /* DFGValidate.cpp in Sources */,
</span><span class="cx">                                 0F2BDC4F15228BF300CD8910 /* DFGValueSource.cpp in Sources */,
</span><span class="lines">@@ -8995,6 +9017,7 @@
</span><span class="cx">                                 0F2B66E417B6B5AB00A7AE3F /* JSArrayBufferConstructor.cpp in Sources */,
</span><span class="cx">                                 0F2B66E617B6B5AB00A7AE3F /* JSArrayBufferPrototype.cpp in Sources */,
</span><span class="cx">                                 0F2B66E817B6B5AB00A7AE3F /* JSArrayBufferView.cpp in Sources */,
</span><ins>+                                A5311C371C77CECA00E6B1B6 /* HeapSnapshotBuilder.cpp in Sources */,
</ins><span class="cx">                                 A7BDAECA17F4EA1400F6140C /* JSArrayIterator.cpp in Sources */,
</span><span class="cx">                                 1421359B0A677F4F00A8195E /* JSBase.cpp in Sources */,
</span><span class="cx">                                 86FA9E91142BBB2E001773B7 /* JSBoundFunction.cpp in Sources */,
</span><span class="lines">@@ -9247,6 +9270,7 @@
</span><span class="cx">                                 BCDE3B430E6C832D001453A7 /* Structure.cpp in Sources */,
</span><span class="cx">                                 7E4EE70F0EBB7A5B005934AA /* StructureChain.cpp in Sources */,
</span><span class="cx">                                 2AF7382C18BBBF92008A5A37 /* StructureIDTable.cpp in Sources */,
</span><ins>+                                A54C2AB01C6544EE00A18D78 /* HeapSnapshot.cpp in Sources */,
</ins><span class="cx">                                 C2F0F2D116BAEEE900187C19 /* StructureRareData.cpp in Sources */,
</span><span class="cx">                                 0FB438A319270B1D00E1FBC9 /* StructureSet.cpp in Sources */,
</span><span class="cx">                                 0F766D3815AE4A1C008F363E /* StructureStubClearingWatchpoint.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/Heap.cpp (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/Heap.cpp        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/heap/Heap.cpp        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -33,7 +33,9 @@
</span><span class="cx"> #include "GCIncomingRefCountedSetInlines.h"
</span><span class="cx"> #include "HeapHelperPool.h"
</span><span class="cx"> #include "HeapIterationScope.h"
</span><ins>+#include "HeapProfiler.h"
</ins><span class="cx"> #include "HeapRootVisitor.h"
</span><ins>+#include "HeapSnapshot.h"
</ins><span class="cx"> #include "HeapStatistics.h"
</span><span class="cx"> #include "HeapVerifier.h"
</span><span class="cx"> #include "IncrementalSweeper.h"
</span><span class="lines">@@ -758,6 +760,43 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+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();
+ }
+ }
+}
+
</ins><span class="cx"> void Heap::visitProtectedObjects(HeapRootVisitor& heapRootVisitor)
</span><span class="cx"> {
</span><span class="cx"> GCPHASE(VisitProtectedObjects);
</span><span class="lines">@@ -1124,6 +1163,7 @@
</span><span class="cx"> removeDeadCompilerWorklistEntries();
</span><span class="cx"> deleteUnmarkedCompiledCode();
</span><span class="cx"> deleteSourceProviderCaches();
</span><ins>+ removeDeadHeapSnapshotNodes();
</ins><span class="cx"> notifyIncrementalSweeper();
</span><span class="cx"> writeBarrierCurrentlyExecutingCodeBlocks();
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeaph"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/Heap.h (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/Heap.h        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/heap/Heap.h        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -163,6 +163,8 @@
</span><span class="cx"> void notifyIsSafeToCollect() { m_isSafeToCollect = true; }
</span><span class="cx"> bool isSafeToCollect() const { return m_isSafeToCollect; }
</span><span class="cx">
</span><ins>+ JS_EXPORT_PRIVATE bool isHeapSnapshotting() const;
+
</ins><span class="cx"> JS_EXPORT_PRIVATE void collectAllGarbageIfNotDoneRecently();
</span><span class="cx"> void collectAllGarbage() { collectAndSweep(FullCollection); }
</span><span class="cx"> JS_EXPORT_PRIVATE void collectAndSweep(HeapOperation collectionType = AnyCollection);
</span><span class="lines">@@ -327,6 +329,7 @@
</span><span class="cx"> void sweepArrayBuffers();
</span><span class="cx"> void snapshotMarkedSpace();
</span><span class="cx"> void deleteSourceProviderCaches();
</span><ins>+ void removeDeadHeapSnapshotNodes();
</ins><span class="cx"> void notifyIncrementalSweeper();
</span><span class="cx"> void writeBarrierCurrentlyExecutingCodeBlocks();
</span><span class="cx"> void resetAllocators();
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapProfilercpp"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/heap/HeapProfiler.cpp (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/HeapProfiler.cpp         (rev 0)
+++ trunk/Source/JavaScriptCore/heap/HeapProfiler.cpp        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,66 @@
</span><ins>+/*
+ * 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
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapProfilerh"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/heap/HeapProfiler.h (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/HeapProfiler.h         (rev 0)
+++ trunk/Source/JavaScriptCore/heap/HeapProfiler.h        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,60 @@
</span><ins>+/*
+ * 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
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapSnapshotcpp"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/heap/HeapSnapshot.cpp (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/HeapSnapshot.cpp         (rev 0)
+++ trunk/Source/JavaScriptCore/heap/HeapSnapshot.cpp        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,175 @@
</span><ins>+/*
+ * 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
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapSnapshoth"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/heap/HeapSnapshot.h (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/HeapSnapshot.h         (rev 0)
+++ trunk/Source/JavaScriptCore/heap/HeapSnapshot.h        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,65 @@
</span><ins>+/*
+ * 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
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapSnapshotBuildercpp"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp         (rev 0)
+++ trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.cpp        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,284 @@
</span><ins>+/*
+ * 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
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapSnapshotBuilderh"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.h (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.h         (rev 0)
+++ trunk/Source/JavaScriptCore/heap/HeapSnapshotBuilder.h        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,107 @@
</span><ins>+/*
+ * 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
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapSlotVisitorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/SlotVisitor.cpp (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/SlotVisitor.cpp        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/heap/SlotVisitor.cpp        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -31,6 +31,8 @@
</span><span class="cx"> #include "CopiedBlockInlines.h"
</span><span class="cx"> #include "CopiedSpace.h"
</span><span class="cx"> #include "CopiedSpaceInlines.h"
</span><ins>+#include "HeapProfiler.h"
+#include "HeapSnapshotBuilder.h"
</ins><span class="cx"> #include "JSArray.h"
</span><span class="cx"> #include "JSDestructibleObject.h"
</span><span class="cx"> #include "VM.h"
</span><span class="lines">@@ -94,6 +96,9 @@
</span><span class="cx"> {
</span><span class="cx"> if (heap()->operationInProgress() == FullCollection)
</span><span class="cx"> ASSERT(m_opaqueRoots.isEmpty()); // Should have merged by now.
</span><ins>+
+ if (HeapProfiler* heapProfiler = vm().heapProfiler())
+ m_heapSnapshotBuilder = heapProfiler->activeSnapshotBuilder();
</ins><span class="cx"> }
</span><span class="cx">
</span><span class="cx"> void SlotVisitor::reset()
</span><span class="lines">@@ -101,6 +106,8 @@
</span><span class="cx"> m_bytesVisited = 0;
</span><span class="cx"> m_bytesCopied = 0;
</span><span class="cx"> m_visitCount = 0;
</span><ins>+ m_heapSnapshotBuilder = nullptr;
+ ASSERT(!m_currentCell);
</ins><span class="cx"> ASSERT(m_stack.isEmpty());
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -121,6 +128,10 @@
</span><span class="cx"> {
</span><span class="cx"> if (!value || !value.isCell())
</span><span class="cx"> return;
</span><ins>+
+ if (m_heapSnapshotBuilder)
+ m_heapSnapshotBuilder->appendEdge(m_currentCell, value.asCell());
+
</ins><span class="cx"> setMarkedAndAppendToMarkStack(value.asCell());
</span><span class="cx"> }
</span><span class="cx">
</span><span class="lines">@@ -155,12 +166,37 @@
</span><span class="cx"> m_visitCount++;
</span><span class="cx"> m_bytesVisited += MarkedBlock::blockFor(cell)->cellSize();
</span><span class="cx"> m_stack.append(cell);
</span><ins>+
+ if (m_heapSnapshotBuilder)
+ m_heapSnapshotBuilder->appendNode(cell);
</ins><span class="cx"> }
</span><span class="cx">
</span><ins>+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;
+};
+
+
</ins><span class="cx"> ALWAYS_INLINE void SlotVisitor::visitChildren(const JSCell* cell)
</span><span class="cx"> {
</span><span class="cx"> ASSERT(Heap::isMarked(cell));
</span><span class="cx">
</span><ins>+ SetCurrentCellScope currentCellScope(*this, cell);
+
</ins><span class="cx"> m_currentObjectCellStateBeforeVisiting = cell->cellState();
</span><span class="cx"> cell->setCellState(CellState::OldBlack);
</span><span class="cx">
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapSlotVisitorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/SlotVisitor.h (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/SlotVisitor.h        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/heap/SlotVisitor.h        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -37,6 +37,7 @@
</span><span class="cx"> class ConservativeRoots;
</span><span class="cx"> class GCThreadSharedData;
</span><span class="cx"> class Heap;
</span><ins>+class HeapSnapshotBuilder;
</ins><span class="cx"> template<typename T> class JITWriteBarrier;
</span><span class="cx"> class UnconditionalFinalizer;
</span><span class="cx"> template<typename T> class Weak;
</span><span class="lines">@@ -47,6 +48,7 @@
</span><span class="cx"> WTF_MAKE_NONCOPYABLE(SlotVisitor);
</span><span class="cx"> WTF_MAKE_FAST_ALLOCATED;
</span><span class="cx">
</span><ins>+ friend class SetCurrentCellScope;
</ins><span class="cx"> friend class HeapRootVisitor; // Allowed to mark a JSValue* or JSCell** directly.
</span><span class="cx"> friend class Heap;
</span><span class="cx">
</span><span class="lines">@@ -111,6 +113,8 @@
</span><span class="cx">
</span><span class="cx"> void dump(PrintStream&) const;
</span><span class="cx">
</span><ins>+ bool isBuildingHeapSnapshot() const { return !!m_heapSnapshotBuilder; }
+
</ins><span class="cx"> private:
</span><span class="cx"> friend class ParallelModeEnabler;
</span><span class="cx">
</span><span class="lines">@@ -137,6 +141,9 @@
</span><span class="cx">
</span><span class="cx"> Heap& m_heap;
</span><span class="cx">
</span><ins>+ HeapSnapshotBuilder* m_heapSnapshotBuilder { nullptr };
+ JSCell* m_currentCell { nullptr };
+
</ins><span class="cx"> CellState m_currentObjectCellStateBeforeVisiting { CellState::NewWhite };
</span><span class="cx">
</span><span class="cx"> public:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorejsccpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/jsc.cpp (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/jsc.cpp        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/jsc.cpp        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -32,6 +32,8 @@
</span><span class="cx"> #include "Disassembler.h"
</span><span class="cx"> #include "Exception.h"
</span><span class="cx"> #include "ExceptionHelpers.h"
</span><ins>+#include "HeapProfiler.h"
+#include "HeapSnapshotBuilder.h"
</ins><span class="cx"> #include "HeapStatistics.h"
</span><span class="cx"> #include "InitializeThreading.h"
</span><span class="cx"> #include "Interpreter.h"
</span><span class="lines">@@ -465,12 +467,67 @@
</span><span class="cx"> Vector<int> m_vector;
</span><span class="cx"> };
</span><span class="cx">
</span><ins>+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;
+};
+
+
</ins><span class="cx"> const ClassInfo Element::s_info = { "Element", &Base::s_info, 0, CREATE_METHOD_TABLE(Element) };
</span><span class="cx"> const ClassInfo Masquerader::s_info = { "Masquerader", &Base::s_info, 0, CREATE_METHOD_TABLE(Masquerader) };
</span><span class="cx"> const ClassInfo Root::s_info = { "Root", &Base::s_info, 0, CREATE_METHOD_TABLE(Root) };
</span><span class="cx"> const ClassInfo ImpureGetter::s_info = { "ImpureGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(ImpureGetter) };
</span><span class="cx"> const ClassInfo CustomGetter::s_info = { "CustomGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(CustomGetter) };
</span><span class="cx"> const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &Base::s_info, 0, CREATE_METHOD_TABLE(RuntimeArray) };
</span><ins>+const ClassInfo SimpleObject::s_info = { "SimpleObject", &Base::s_info, 0, CREATE_METHOD_TABLE(SimpleObject) };
</ins><span class="cx">
</span><span class="cx"> ElementHandleOwner* Element::handleOwner()
</span><span class="cx"> {
</span><span class="lines">@@ -501,6 +558,9 @@
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionCreateRoot(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionCreateElement(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionGetElement(ExecState*);
</span><ins>+static EncodedJSValue JSC_HOST_CALL functionCreateSimpleObject(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionGetHiddenValue(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionSetHiddenValue(ExecState*);
</ins><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
</span><span class="lines">@@ -555,6 +615,7 @@
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionLoadModule(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionPlatformSupportsSamplingProfiler(ExecState*);
</span><ins>+static EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshot(ExecState*);
</ins><span class="cx"> #if ENABLE(SAMPLING_PROFILER)
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionStartSamplingProfiler(ExecState*);
</span><span class="cx"> static EncodedJSValue JSC_HOST_CALL functionSamplingProfilerStackTraces(ExecState*);
</span><span class="lines">@@ -708,6 +769,10 @@
</span><span class="cx"> addFunction(vm, "getElement", functionGetElement, 1);
</span><span class="cx"> addFunction(vm, "setElementRoot", functionSetElementRoot, 2);
</span><span class="cx">
</span><ins>+ addConstructableFunction(vm, "SimpleObject", functionCreateSimpleObject, 0);
+ addFunction(vm, "getHiddenValue", functionGetHiddenValue, 1);
+ addFunction(vm, "setHiddenValue", functionSetHiddenValue, 2);
+
</ins><span class="cx"> putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "DFGTrue"), 0, functionFalse1, DFGTrueIntrinsic, DontEnum);
</span><span class="cx"> putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "OSRExit"), 0, functionUndefined1, OSRExitIntrinsic, DontEnum);
</span><span class="cx"> putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "isFinalTier"), 0, functionFalse2, IsFinalTierIntrinsic, DontEnum);
</span><span class="lines">@@ -747,6 +812,7 @@
</span><span class="cx"> addFunction(vm, "checkModuleSyntax", functionCheckModuleSyntax, 1);
</span><span class="cx">
</span><span class="cx"> addFunction(vm, "platformSupportsSamplingProfiler", functionPlatformSupportsSamplingProfiler, 0);
</span><ins>+ addFunction(vm, "generateHeapSnapshot", functionGenerateHeapSnapshot, 0);
</ins><span class="cx"> #if ENABLE(SAMPLING_PROFILER)
</span><span class="cx"> addFunction(vm, "startSamplingProfiler", functionStartSamplingProfiler, 0);
</span><span class="cx"> addFunction(vm, "samplingProfilerStackTraces", functionSamplingProfilerStackTraces, 0);
</span><span class="lines">@@ -1133,6 +1199,28 @@
</span><span class="cx"> return JSValue::encode(jsUndefined());
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+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());
+}
+
</ins><span class="cx"> EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState* exec)
</span><span class="cx"> {
</span><span class="cx"> JSLockHolder lock(exec);
</span><span class="lines">@@ -1646,6 +1734,19 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+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;
+}
+
</ins><span class="cx"> #if ENABLE(SAMPLING_PROFILER)
</span><span class="cx"> EncodedJSValue JSC_HOST_CALL functionStartSamplingProfiler(ExecState* exec)
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeVMcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/VM.cpp (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/VM.cpp        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/runtime/VM.cpp        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -50,6 +50,7 @@
</span><span class="cx"> #include "GetterSetter.h"
</span><span class="cx"> #include "Heap.h"
</span><span class="cx"> #include "HeapIterationScope.h"
</span><ins>+#include "HeapProfiler.h"
</ins><span class="cx"> #include "HostCallReturnValue.h"
</span><span class="cx"> #include "Identifier.h"
</span><span class="cx"> #include "IncrementalSweeper.h"
</span><span class="lines">@@ -443,6 +444,13 @@
</span><span class="cx"> return *m_watchdog;
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+HeapProfiler& VM::ensureHeapProfiler()
+{
+ if (!m_heapProfiler)
+ m_heapProfiler = std::make_unique<HeapProfiler>(*this);
+ return *m_heapProfiler;
+}
+
</ins><span class="cx"> #if ENABLE(SAMPLING_PROFILER)
</span><span class="cx"> void VM::ensureSamplingProfiler(RefPtr<Stopwatch>&& stopwatch)
</span><span class="cx"> {
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeVMh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/VM.h (197488 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/VM.h        2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/JavaScriptCore/runtime/VM.h        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -87,6 +87,7 @@
</span><span class="cx"> class HandleStack;
</span><span class="cx"> class TypeProfiler;
</span><span class="cx"> class TypeProfilerLog;
</span><ins>+class HeapProfiler;
</ins><span class="cx"> class Identifier;
</span><span class="cx"> class Interpreter;
</span><span class="cx"> class JSBoundSlotBaseFunction;
</span><span class="lines">@@ -249,6 +250,9 @@
</span><span class="cx"> JS_EXPORT_PRIVATE Watchdog& ensureWatchdog();
</span><span class="cx"> JS_EXPORT_PRIVATE Watchdog* watchdog() { return m_watchdog.get(); }
</span><span class="cx">
</span><ins>+ JS_EXPORT_PRIVATE HeapProfiler* heapProfiler() const { return m_heapProfiler.get(); }
+ JS_EXPORT_PRIVATE HeapProfiler& ensureHeapProfiler();
+
</ins><span class="cx"> #if ENABLE(SAMPLING_PROFILER)
</span><span class="cx"> JS_EXPORT_PRIVATE SamplingProfiler* samplingProfiler() { return m_samplingProfiler.get(); }
</span><span class="cx"> JS_EXPORT_PRIVATE void ensureSamplingProfiler(RefPtr<Stopwatch>&&);
</span><span class="lines">@@ -672,6 +676,7 @@
</span><span class="cx"> Deque<std::unique_ptr<QueuedTask>> m_microtaskQueue;
</span><span class="cx"> MallocPtr<EncodedJSValue> m_exceptionFuzzBuffer;
</span><span class="cx"> RefPtr<Watchdog> m_watchdog;
</span><ins>+ std::unique_ptr<HeapProfiler> m_heapProfiler;
</ins><span class="cx"> #if ENABLE(SAMPLING_PROFILER)
</span><span class="cx"> RefPtr<SamplingProfiler> m_samplingProfiler;
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsheapProfilerbasicedgesjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/heapProfiler/basic-edges.js (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/heapProfiler/basic-edges.js         (rev 0)
+++ trunk/Source/JavaScriptCore/tests/heapProfiler/basic-edges.js        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+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");
+})();
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsheapProfilerbasicnodesjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/heapProfiler/basic-nodes.js (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/heapProfiler/basic-nodes.js         (rev 0)
+++ trunk/Source/JavaScriptCore/tests/heapProfiler/basic-nodes.js        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,78 @@
</span><ins>+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");
+})();
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsheapProfilerdriverdriverjs"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/heapProfiler/driver/driver.js (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/heapProfiler/driver/driver.js         (rev 0)
+++ trunk/Source/JavaScriptCore/tests/heapProfiler/driver/driver.js        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,242 @@
</span><ins>+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 = edges[edgeIndex + 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 = 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);
+}
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsheapProfileryaml"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/tests/heapProfiler.yaml (0 => 197489)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/heapProfiler.yaml         (rev 0)
+++ trunk/Source/JavaScriptCore/tests/heapProfiler.yaml        2016-03-03 05:15:56 UTC (rev 197489)
</span><span class="lines">@@ -0,0 +1,25 @@
</span><ins>+# 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
</ins></span></pre>
</div>
</div>
</body>
</html>