Diff
Modified: trunk/Source/_javascript_Core/CMakeLists.txt (197488 => 197489)
--- trunk/Source/_javascript_Core/CMakeLists.txt 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/CMakeLists.txt 2016-03-03 05:15:56 UTC (rev 197489)
@@ -443,6 +443,9 @@
heap/HandleStack.cpp
heap/Heap.cpp
heap/HeapHelperPool.cpp
+ heap/HeapProfiler.cpp
+ heap/HeapSnapshot.cpp
+ heap/HeapSnapshotBuilder.cpp
heap/HeapStatistics.cpp
heap/HeapTimer.cpp
heap/HeapVerifier.cpp
Modified: trunk/Source/_javascript_Core/ChangeLog (197488 => 197489)
--- trunk/Source/_javascript_Core/ChangeLog 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-03-03 05:15:56 UTC (rev 197489)
@@ -1,3 +1,196 @@
+2016-03-02 Joseph Pecoraro <[email protected]>
+
+ Add ability to generate a Heap Snapshot
+ https://bugs.webkit.org/show_bug.cgi?id=154847
+
+ Reviewed by Mark Lam.
+
+ This adds HeapSnapshot, HeapSnapshotBuilder, and HeapProfiler.
+
+ HeapProfiler hangs off of the VM and holds the list of snapshots.
+ I expect to add other HeapProfiling features, such as allocation
+ tracking, to the profiler.
+
+ HeapSnapshot contains a collection of live cells and their identifiers.
+ It can point to a previous HeapSnapshot, to ensure that a cell that
+ already received an identifier maintains the same identifier across
+ multiple snapshots. When a snapshotted cell gets garbage collected,
+ the cell will be swept from the HeapSnapshot at the end of collection
+ to ensure the list contains only live cells.
+
+ When building a HeapSnapshot nodes are added in increasing node
+ identifier order. When done building, the list of nodes is complete
+ and the snapshot is finalized. At this point the nodes are sorted
+ by JSCell* address to allow for quick lookup of a JSCell*.
+
+ HeapSnapshotBuilder is where snapshotting begins. The builder
+ will initiate a specialized heap snapshotting garbage collection.
+ During this collection the builder will be notified of all marked
+ (live) cells, and connections between cells, as seen by SlotVisitors.
+ The builder can reference the previous, readonly, HeapSnapshots to
+ avoid creating new nodes for cells that have already been snapshotted.
+ When it is determined that we are visiting a live cell for the first
+ time, we give the cell a unique identifier and add it to the the
+ snapshot we are building.
+
+ Since edge data is costly, and of little long term utility, this
+ data is only held by the builder for serialization, and not stored
+ long term with the HeapSnapshot node data.
+
+ The goals of HeapSnapshotting at this time are:
+ - minimal impact on performance when not profiling the heap
+ - unique identifier for cells, so they may be identified across multiple snapshots
+ - nodes and edges to be able to construct a graph of which nodes reference/retain which other nodes
+ - node data - identifier, type (class name), size
+ - edge data - from cell, to cell, type / data (to come in a follow-up patch)
+
+ * CMakeLists.txt:
+ * _javascript_Core.xcodeproj/project.pbxproj:
+ * _javascript_Core.vcxproj/_javascript_Core.vcxproj:
+ * _javascript_Core.vcxproj/_javascript_Core.vcxproj.filters:
+ Add new files to the build.
+
+ * heap/Heap.cpp:
+ (JSC::Heap::isHeapSnapshotting):
+ (JSC::RemoveDeadHeapSnapshotNodes::RemoveDeadHeapSnapshotNodes):
+ (JSC::RemoveDeadHeapSnapshotNodes::operator()):
+ (JSC::Heap::removeDeadHeapSnapshotNodes):
+ (JSC::Heap::collectImpl):
+ After every collection, sweep dead cells from in memory snapshots.
+
+ * runtime/VM.cpp:
+ (JSC::VM::ensureHeapProfiler):
+ * runtime/VM.h:
+ (JSC::VM::heapProfiler):
+ * heap/Heap.h:
+ * heap/HeapProfiler.cpp: Added.
+ (JSC::HeapProfiler::HeapProfiler):
+ (JSC::HeapProfiler::~HeapProfiler):
+ (JSC::HeapProfiler::mostRecentSnapshot):
+ (JSC::HeapProfiler::appendSnapshot):
+ (JSC::HeapProfiler::clearSnapshots):
+ (JSC::HeapProfiler::setActiveSnapshotBuilder):
+ * heap/HeapProfiler.h: Added.
+ (JSC::HeapProfiler::vm):
+ (JSC::HeapProfiler::activeSnapshotBuilder):
+ VM and Heap can look at the profiler to determine if we are building a
+ snapshot, or the "head" snapshot to use for sweeping.
+
+ * heap/HeapSnapshot.cpp: Added.
+ (JSC::HeapSnapshot::HeapSnapshot):
+ (JSC::HeapSnapshot::~HeapSnapshot):
+ (JSC::HeapSnapshot::appendNode):
+ Add a node to the unfinalized list of new cells.
+
+ (JSC::HeapSnapshot::sweepCell):
+ (JSC::HeapSnapshot::shrinkToFit):
+ Collect a list of cells for sweeping and then remove them all at once
+ in shrinkToFit. This is done to avoid thrashing of individual removes
+ that could cause many overlapping moves within the Vector.
+
+ (JSC::HeapSnapshot::finalize):
+ Sort the list, and also cache the bounding start/stop identifiers.
+ No other snapshot can contain an identifier in this range, so it will
+ improve lookup of a node from an identifier.
+
+ (JSC::HeapSnapshot::nodeForCell):
+ (JSC::HeapSnapshot::nodeForObjectIdentifier):
+ Search helpers.
+
+ * heap/HeapSnapshotBuilder.h: Added.
+ (JSC::HeapSnapshotNode::HeapSnapshotNode):
+ (JSC::HeapSnapshotEdge::HeapSnapshotEdge):
+ Node and Edge struct types the builder creates.
+
+ * heap/HeapSnapshotBuilder.cpp: Added.
+ (JSC::HeapSnapshotBuilder::getNextObjectIdentifier):
+ (JSC::HeapSnapshotBuilder::HeapSnapshotBuilder):
+ (JSC::HeapSnapshotBuilder::~HeapSnapshotBuilder):
+ (JSC::HeapSnapshotBuilder::buildSnapshot):
+ (JSC::HeapSnapshotBuilder::appendNode):
+ (JSC::HeapSnapshotBuilder::appendEdge):
+ When building the snapshot, generating the next identifier, and
+ appending to any of the lists must be guarded by a lock because
+ SlotVisitors running in parallel may be accessing the builder.
+
+ (JSC::HeapSnapshotBuilder::hasExistingNodeForCell):
+ Looking up if a node already exists in a previous snapshot can be
+ done without a lock because at this point the data is readonly.
+
+ (JSC::edgeTypeToNumber):
+ (JSC::edgeTypeToString):
+ (JSC::HeapSnapshotBuilder::json):
+ JSON serialization of a heap snapshot contains node and edge data.
+
+ * heap/SlotVisitor.h:
+ * heap/SlotVisitor.cpp:
+ (JSC::SlotVisitor::didStartMarking):
+ (JSC::SlotVisitor::reset):
+ Set/clear the active snapshot builder to know if this will be a
+ snapshotting GC or not.
+
+ (JSC::SlotVisitor::append):
+ (JSC::SlotVisitor::setMarkedAndAppendToMarkStack):
+ Inform the builder of a new node or edge.
+
+ (JSC::SlotVisitor::visitChildren):
+ Remember the current cell we are visiting so that if we need to
+ inform the builder of edges we know the "from" cell.
+
+ * jsc.cpp:
+ (SimpleObject::SimpleObject):
+ (SimpleObject::create):
+ (SimpleObject::finishCreation):
+ (SimpleObject::visitChildren):
+ (SimpleObject::createStructure):
+ (SimpleObject::hiddenValue):
+ (SimpleObject::setHiddenValue):
+ Create a new class "SimpleObject" that can be used by heap snapshotting
+ tests. It is easy to filter for this new class name and test internal
+ edge relationships created by garbage collection visiting the cell.
+
+ (functionCreateSimpleObject):
+ (functionGetHiddenValue):
+ (functionSetHiddenValue):
+ Expose methods to create and interact with a SimpleObject.
+
+ (functionGenerateHeapSnapshot):
+ Expose methods to create a heap snapshot. This currently automatically
+ turns the serialized string into a JSON object. That may change.
+
+ * tests/heapProfiler.yaml: Added.
+ * tests/heapProfiler/basic-edges.js: Added.
+ (excludeStructure):
+ * tests/heapProfiler/basic-nodes.js: Added.
+ (hasDifferentSizeNodes):
+ (hasAllInternalNodes):
+ Add tests for basic node and edge data.
+
+ * tests/heapProfiler/driver/driver.js: Added.
+ (assert):
+ (CheapHeapSnapshotNode):
+ (CheapHeapSnapshotEdge):
+ (CheapHeapSnapshotEdge.prototype.get from):
+ (CheapHeapSnapshotEdge.prototype.get to):
+ (CheapHeapSnapshot):
+ (CheapHeapSnapshot.prototype.get nodes):
+ (CheapHeapSnapshot.prototype.get edges):
+ (CheapHeapSnapshot.prototype.nodeWithIdentifier):
+ (CheapHeapSnapshot.prototype.nodesWithClassName):
+ (CheapHeapSnapshot.prototype.classNameFromTableIndex):
+ (CheapHeapSnapshot.prototype.edgeTypeFromTableIndex):
+ (createCheapHeapSnapshot):
+ (HeapSnapshotNode):
+ (HeapSnapshotEdge):
+ (HeapSnapshot):
+ (HeapSnapshot.prototype.nodesWithClassName):
+ (createHeapSnapshot):
+ Add two HeapSnapshot representations.
+ CheapHeapSnapshot creates two lists of node and edge data that
+ lazily creates objects as needed.
+ HeapSnapshot creates an object for each node and edge. This
+ is wasteful but easier to use.
+
2016-03-02 Filip Pizlo <[email protected]>
RegExpPrototype should check for exceptions after calling toString and doing so should not be expensive
Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj (197488 => 197489)
--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj 2016-03-03 05:15:56 UTC (rev 197489)
@@ -575,6 +575,9 @@
<ClCompile Include="..\heap\HandleStack.cpp" />
<ClCompile Include="..\heap\Heap.cpp" />
<ClCompile Include="..\heap\HeapHelperPool.cpp" />
+ <ClCompile Include="..\heap\HeapProfiler.cpp" />
+ <ClCompile Include="..\heap\HeapSnapshot.cpp" />
+ <ClCompile Include="..\heap\HeapSnapshotBuilder.cpp" />
<ClCompile Include="..\heap\HeapStatistics.cpp" />
<ClCompile Include="..\heap\HeapTimer.cpp" />
<ClCompile Include="..\heap\HeapVerifier.cpp" />
@@ -1386,6 +1389,9 @@
<ClInclude Include="..\heap\HeapInlines.h" />
<ClInclude Include="..\heap\HeapOperation.h" />
<ClInclude Include="..\heap\HeapRootVisitor.h" />
+ <ClInclude Include="..\heap\HeapProfiler.h" />
+ <ClInclude Include="..\heap\HeapSnapshot.h" />
+ <ClInclude Include="..\heap\HeapSnapshotBuilder.h" />
<ClInclude Include="..\heap\HeapStatistics.h" />
<ClInclude Include="..\heap\HeapTimer.h" />
<ClInclude Include="..\heap\HeapVerifier.h" />
Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters (197488 => 197489)
--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters 2016-03-03 05:15:56 UTC (rev 197489)
@@ -294,6 +294,15 @@
<ClCompile Include="..\heap\HeapHelperPool.cpp">
<Filter>heap</Filter>
</ClCompile>
+ <ClCompile Include="..\heap\HeapProfiler.cpp">
+ <Filter>heap</Filter>
+ </ClCompile>
+ <ClCompile Include="..\heap\HeapSnapshot.cpp">
+ <Filter>heap</Filter>
+ </ClCompile>
+ <ClCompile Include="..\heap\HeapSnapshotBuilder.cpp">
+ <Filter>heap</Filter>
+ </ClCompile>
<ClCompile Include="..\heap\HeapStatistics.cpp">
<Filter>heap</Filter>
</ClCompile>
@@ -2368,6 +2377,15 @@
<ClInclude Include="..\heap\HeapRootVisitor.h">
<Filter>heap</Filter>
</ClInclude>
+ <ClInclude Include="..\heap\HeapProfiler.h">
+ <Filter>heap</Filter>
+ </ClInclude>
+ <ClInclude Include="..\heap\HeapSnapshot.h">
+ <Filter>heap</Filter>
+ </ClInclude>
+ <ClInclude Include="..\heap\HeapSnapshotBuilder.h">
+ <Filter>heap</Filter>
+ </ClInclude>
<ClInclude Include="..\heap\HeapStatistics.h">
<Filter>heap</Filter>
</ClInclude>
Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (197488 => 197489)
--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj 2016-03-03 05:15:56 UTC (rev 197489)
@@ -1548,6 +1548,8 @@
A513E5CB185F9624007E95AD /* InjectedScriptManager.h in Headers */ = {isa = PBXBuildFile; fileRef = A513E5C9185F9624007E95AD /* InjectedScriptManager.h */; settings = {ATTRIBUTES = (Private, ); }; };
A514B2C2185A684400F3C7CB /* InjectedScriptBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A514B2C0185A684400F3C7CB /* InjectedScriptBase.cpp */; };
A514B2C3185A684400F3C7CB /* InjectedScriptBase.h in Headers */ = {isa = PBXBuildFile; fileRef = A514B2C1185A684400F3C7CB /* InjectedScriptBase.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ A5311C361C77CEC500E6B1B6 /* HeapSnapshotBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ A5311C371C77CECA00E6B1B6 /* HeapSnapshotBuilder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */; };
A532438718568335002ED692 /* InspectorBackendDispatchers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A532438118568317002ED692 /* InspectorBackendDispatchers.cpp */; };
A532438818568335002ED692 /* InspectorBackendDispatchers.h in Headers */ = {isa = PBXBuildFile; fileRef = A532438218568317002ED692 /* InspectorBackendDispatchers.h */; settings = {ATTRIBUTES = (Private, ); }; };
A532438918568335002ED692 /* InspectorFrontendDispatchers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A532438318568317002ED692 /* InspectorFrontendDispatchers.cpp */; };
@@ -1558,6 +1560,8 @@
A5339EC61BB399A60054F005 /* InspectorHeapAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = A5339EC51BB399900054F005 /* InspectorHeapAgent.h */; settings = {ATTRIBUTES = (Private, ); }; };
A5339EC71BB399A90054F005 /* InspectorHeapAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5339EC41BB399900054F005 /* InspectorHeapAgent.cpp */; };
A5339EC91BB4B4600054F005 /* HeapObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = A5339EC81BB4B4510054F005 /* HeapObserver.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ A5398FAB1C750DA40060A963 /* HeapProfiler.h in Headers */ = {isa = PBXBuildFile; fileRef = A5398FAA1C750D950060A963 /* HeapProfiler.h */; };
+ A5398FAC1C750DA60060A963 /* HeapProfiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A5398FA91C750D950060A963 /* HeapProfiler.cpp */; };
A53CE08518BC1A5600BEDF76 /* ConsolePrototype.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A53CE08118BC1A5600BEDF76 /* ConsolePrototype.cpp */; };
A53CE08618BC1A5600BEDF76 /* ConsolePrototype.h in Headers */ = {isa = PBXBuildFile; fileRef = A53CE08218BC1A5600BEDF76 /* ConsolePrototype.h */; };
A53CE08718BC1A5600BEDF76 /* JSConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A53CE08318BC1A5600BEDF76 /* JSConsole.cpp */; };
@@ -1566,6 +1570,8 @@
A53F1AC018C90F8F0072EB6D /* framework.sb in Resources */ = {isa = PBXBuildFile; fileRef = A53F1ABE18C90EC70072EB6D /* framework.sb */; };
A54982031891D0B00081E5B8 /* EventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54982011891D0B00081E5B8 /* EventLoop.cpp */; };
A54982041891D0B00081E5B8 /* EventLoop.h in Headers */ = {isa = PBXBuildFile; fileRef = A54982021891D0B00081E5B8 /* EventLoop.h */; };
+ A54C2AB01C6544EE00A18D78 /* HeapSnapshot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54C2AAE1C6544D100A18D78 /* HeapSnapshot.cpp */; };
+ A54C2AB11C6544F200A18D78 /* HeapSnapshot.h in Headers */ = {isa = PBXBuildFile; fileRef = A54C2AAF1C6544D100A18D78 /* HeapSnapshot.h */; settings = {ATTRIBUTES = (Private, ); }; };
A54CF2F5184EAB2400237F19 /* ScriptValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54CF2F2184EAB2400237F19 /* ScriptValue.cpp */; };
A54CF2F6184EAB2400237F19 /* ScriptValue.h in Headers */ = {isa = PBXBuildFile; fileRef = A54CF2F3184EAB2400237F19 /* ScriptValue.h */; settings = {ATTRIBUTES = (Private, ); }; };
A54CF2F9184EAEDA00237F19 /* ScriptObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A54CF2F7184EAEDA00237F19 /* ScriptObject.cpp */; };
@@ -3743,6 +3749,8 @@
A513E5C9185F9624007E95AD /* InjectedScriptManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptManager.h; sourceTree = "<group>"; };
A514B2C0185A684400F3C7CB /* InjectedScriptBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InjectedScriptBase.cpp; sourceTree = "<group>"; };
A514B2C1185A684400F3C7CB /* InjectedScriptBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptBase.h; sourceTree = "<group>"; };
+ A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapSnapshotBuilder.cpp; sourceTree = "<group>"; };
+ A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapSnapshotBuilder.h; sourceTree = "<group>"; };
A532438118568317002ED692 /* InspectorBackendDispatchers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorBackendDispatchers.cpp; sourceTree = "<group>"; };
A532438218568317002ED692 /* InspectorBackendDispatchers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InspectorBackendDispatchers.h; sourceTree = "<group>"; };
A532438318568317002ED692 /* InspectorFrontendDispatchers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorFrontendDispatchers.cpp; sourceTree = "<group>"; };
@@ -3755,6 +3763,8 @@
A5339EC41BB399900054F005 /* InspectorHeapAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorHeapAgent.cpp; sourceTree = "<group>"; };
A5339EC51BB399900054F005 /* InspectorHeapAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorHeapAgent.h; sourceTree = "<group>"; };
A5339EC81BB4B4510054F005 /* HeapObserver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapObserver.h; sourceTree = "<group>"; };
+ A5398FA91C750D950060A963 /* HeapProfiler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapProfiler.cpp; sourceTree = "<group>"; };
+ A5398FAA1C750D950060A963 /* HeapProfiler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapProfiler.h; sourceTree = "<group>"; };
A53CE08118BC1A5600BEDF76 /* ConsolePrototype.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConsolePrototype.cpp; sourceTree = "<group>"; };
A53CE08218BC1A5600BEDF76 /* ConsolePrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConsolePrototype.h; sourceTree = "<group>"; };
A53CE08318BC1A5600BEDF76 /* JSConsole.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSConsole.cpp; sourceTree = "<group>"; };
@@ -3763,6 +3773,8 @@
A53F1ABE18C90EC70072EB6D /* framework.sb */ = {isa = PBXFileReference; lastKnownFileType = text; path = framework.sb; sourceTree = "<group>"; };
A54982011891D0B00081E5B8 /* EventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EventLoop.cpp; sourceTree = "<group>"; };
A54982021891D0B00081E5B8 /* EventLoop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EventLoop.h; sourceTree = "<group>"; };
+ A54C2AAE1C6544D100A18D78 /* HeapSnapshot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = HeapSnapshot.cpp; sourceTree = "<group>"; };
+ A54C2AAF1C6544D100A18D78 /* HeapSnapshot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapSnapshot.h; sourceTree = "<group>"; };
A54CF2F2184EAB2400237F19 /* ScriptValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptValue.cpp; sourceTree = "<group>"; };
A54CF2F3184EAB2400237F19 /* ScriptValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScriptValue.h; sourceTree = "<group>"; };
A54CF2F7184EAEDA00237F19 /* ScriptObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScriptObject.cpp; sourceTree = "<group>"; };
@@ -5093,7 +5105,13 @@
2AD8932917E3868F00668276 /* HeapIterationScope.h */,
A5339EC81BB4B4510054F005 /* HeapObserver.h */,
2A6F462517E959CE00C45C98 /* HeapOperation.h */,
+ A5398FA91C750D950060A963 /* HeapProfiler.cpp */,
+ A5398FAA1C750D950060A963 /* HeapProfiler.h */,
14F97446138C853E00DA1C67 /* HeapRootVisitor.h */,
+ A54C2AAE1C6544D100A18D78 /* HeapSnapshot.cpp */,
+ A54C2AAF1C6544D100A18D78 /* HeapSnapshot.h */,
+ A5311C341C77CEAC00E6B1B6 /* HeapSnapshotBuilder.cpp */,
+ A5311C351C77CEAC00E6B1B6 /* HeapSnapshotBuilder.h */,
C24D31E0161CD695002AA4DB /* HeapStatistics.cpp */,
C24D31E1161CD695002AA4DB /* HeapStatistics.h */,
C2E526BB1590EF000054E48D /* HeapTimer.cpp */,
@@ -6918,6 +6936,7 @@
BC18C3E60E16F5CD00B34460 /* ArrayConstructor.h in Headers */,
996B73171BDA067F00331B84 /* ArrayConstructor.lut.h in Headers */,
0FB7F39515ED8E4600F167B2 /* ArrayConventions.h in Headers */,
+ A5311C361C77CEC500E6B1B6 /* HeapSnapshotBuilder.h in Headers */,
A7BDAEC917F4EA1400F6140C /* ArrayIteratorPrototype.h in Headers */,
996B73181BDA068000331B84 /* ArrayIteratorPrototype.lut.h in Headers */,
0F63945515D07057006A597C /* ArrayProfile.h in Headers */,
@@ -7090,6 +7109,7 @@
41359CF30FDD89AD00206180 /* DateConversion.h in Headers */,
BC1166020E1997B4008066DD /* DateInstance.h in Headers */,
14A1563210966365006FA260 /* DateInstanceCache.h in Headers */,
+ A54C2AB11C6544F200A18D78 /* HeapSnapshot.h in Headers */,
BCD2034C0E17135E002C7E82 /* DatePrototype.h in Headers */,
BCD203E80E1718F4002C7E82 /* DatePrototype.lut.h in Headers */,
BC18C3FA0E16F5CD00B34460 /* Debugger.h in Headers */,
@@ -7502,6 +7522,7 @@
FE187A0F1C030D6C0038BBCA /* SnippetOperand.h in Headers */,
A1587D701B4DC14100D69849 /* IntlDateTimeFormatConstructor.h in Headers */,
A1587D751B4DC1C600D69849 /* IntlDateTimeFormatConstructor.lut.h in Headers */,
+ A5398FAB1C750DA40060A963 /* HeapProfiler.h in Headers */,
A1587D721B4DC14100D69849 /* IntlDateTimeFormatPrototype.h in Headers */,
A1587D761B4DC1C600D69849 /* IntlDateTimeFormatPrototype.lut.h in Headers */,
A1D792FD1B43864B004516F5 /* IntlNumberFormat.h in Headers */,
@@ -8822,6 +8843,7 @@
0FE7211D193B9C590031F6ED /* DFGTransition.cpp in Sources */,
0F63944015C75F1D006A597C /* DFGTypeCheckHoistingPhase.cpp in Sources */,
0FBE0F7616C1DB0F0082C5E8 /* DFGUnificationPhase.cpp in Sources */,
+ A5398FAC1C750DA60060A963 /* HeapProfiler.cpp in Sources */,
0F34B14916D42010001CDA5A /* DFGUseKind.cpp in Sources */,
0F3B3A2B15475000003ED0FF /* DFGValidate.cpp in Sources */,
0F2BDC4F15228BF300CD8910 /* DFGValueSource.cpp in Sources */,
@@ -8995,6 +9017,7 @@
0F2B66E417B6B5AB00A7AE3F /* JSArrayBufferConstructor.cpp in Sources */,
0F2B66E617B6B5AB00A7AE3F /* JSArrayBufferPrototype.cpp in Sources */,
0F2B66E817B6B5AB00A7AE3F /* JSArrayBufferView.cpp in Sources */,
+ A5311C371C77CECA00E6B1B6 /* HeapSnapshotBuilder.cpp in Sources */,
A7BDAECA17F4EA1400F6140C /* JSArrayIterator.cpp in Sources */,
1421359B0A677F4F00A8195E /* JSBase.cpp in Sources */,
86FA9E91142BBB2E001773B7 /* JSBoundFunction.cpp in Sources */,
@@ -9247,6 +9270,7 @@
BCDE3B430E6C832D001453A7 /* Structure.cpp in Sources */,
7E4EE70F0EBB7A5B005934AA /* StructureChain.cpp in Sources */,
2AF7382C18BBBF92008A5A37 /* StructureIDTable.cpp in Sources */,
+ A54C2AB01C6544EE00A18D78 /* HeapSnapshot.cpp in Sources */,
C2F0F2D116BAEEE900187C19 /* StructureRareData.cpp in Sources */,
0FB438A319270B1D00E1FBC9 /* StructureSet.cpp in Sources */,
0F766D3815AE4A1C008F363E /* StructureStubClearingWatchpoint.cpp in Sources */,
Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (197488 => 197489)
--- trunk/Source/_javascript_Core/heap/Heap.cpp 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp 2016-03-03 05:15:56 UTC (rev 197489)
@@ -33,7 +33,9 @@
#include "GCIncomingRefCountedSetInlines.h"
#include "HeapHelperPool.h"
#include "HeapIterationScope.h"
+#include "HeapProfiler.h"
#include "HeapRootVisitor.h"
+#include "HeapSnapshot.h"
#include "HeapStatistics.h"
#include "HeapVerifier.h"
#include "IncrementalSweeper.h"
@@ -758,6 +760,43 @@
#endif
}
+bool Heap::isHeapSnapshotting() const
+{
+ HeapProfiler* heapProfiler = m_vm->heapProfiler();
+ if (UNLIKELY(heapProfiler))
+ return heapProfiler->activeSnapshotBuilder();
+ return false;
+}
+
+struct RemoveDeadHeapSnapshotNodes : MarkedBlock::CountFunctor {
+ RemoveDeadHeapSnapshotNodes(HeapSnapshot& snapshot)
+ : m_snapshot(snapshot)
+ {
+ }
+
+ IterationStatus operator()(JSCell* cell)
+ {
+ m_snapshot.sweepCell(cell);
+ return IterationStatus::Continue;
+ }
+
+ HeapSnapshot& m_snapshot;
+};
+
+void Heap::removeDeadHeapSnapshotNodes()
+{
+ GCPHASE(RemoveDeadHeapSnapshotNodes);
+ HeapProfiler* heapProfiler = m_vm->heapProfiler();
+ if (UNLIKELY(heapProfiler)) {
+ if (HeapSnapshot* snapshot = heapProfiler->mostRecentSnapshot()) {
+ HeapIterationScope heapIterationScope(*this);
+ RemoveDeadHeapSnapshotNodes functor(*snapshot);
+ m_objectSpace.forEachDeadCell(heapIterationScope, functor);
+ snapshot->shrinkToFit();
+ }
+ }
+}
+
void Heap::visitProtectedObjects(HeapRootVisitor& heapRootVisitor)
{
GCPHASE(VisitProtectedObjects);
@@ -1124,6 +1163,7 @@
removeDeadCompilerWorklistEntries();
deleteUnmarkedCompiledCode();
deleteSourceProviderCaches();
+ removeDeadHeapSnapshotNodes();
notifyIncrementalSweeper();
writeBarrierCurrentlyExecutingCodeBlocks();
Modified: trunk/Source/_javascript_Core/heap/Heap.h (197488 => 197489)
--- trunk/Source/_javascript_Core/heap/Heap.h 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/heap/Heap.h 2016-03-03 05:15:56 UTC (rev 197489)
@@ -163,6 +163,8 @@
void notifyIsSafeToCollect() { m_isSafeToCollect = true; }
bool isSafeToCollect() const { return m_isSafeToCollect; }
+ JS_EXPORT_PRIVATE bool isHeapSnapshotting() const;
+
JS_EXPORT_PRIVATE void collectAllGarbageIfNotDoneRecently();
void collectAllGarbage() { collectAndSweep(FullCollection); }
JS_EXPORT_PRIVATE void collectAndSweep(HeapOperation collectionType = AnyCollection);
@@ -327,6 +329,7 @@
void sweepArrayBuffers();
void snapshotMarkedSpace();
void deleteSourceProviderCaches();
+ void removeDeadHeapSnapshotNodes();
void notifyIncrementalSweeper();
void writeBarrierCurrentlyExecutingCodeBlocks();
void resetAllocators();
Added: trunk/Source/_javascript_Core/heap/HeapProfiler.cpp (0 => 197489)
--- trunk/Source/_javascript_Core/heap/HeapProfiler.cpp (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapProfiler.cpp 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HeapProfiler.h"
+
+#include "HeapSnapshot.h"
+#include "VM.h"
+
+namespace JSC {
+
+HeapProfiler::HeapProfiler(VM& vm)
+ : m_vm(vm)
+{
+}
+
+HeapProfiler::~HeapProfiler()
+{
+}
+
+HeapSnapshot* HeapProfiler::mostRecentSnapshot()
+{
+ if (m_snapshots.isEmpty())
+ return nullptr;
+ return m_snapshots.last().get();
+}
+
+void HeapProfiler::appendSnapshot(std::unique_ptr<HeapSnapshot> snapshot)
+{
+ m_snapshots.append(WTFMove(snapshot));
+}
+
+void HeapProfiler::clearSnapshots()
+{
+ m_snapshots.clear();
+}
+
+void HeapProfiler::setActiveSnapshotBuilder(HeapSnapshotBuilder* builder)
+{
+ ASSERT(!!m_activeBuilder != !!builder);
+ m_activeBuilder = builder;
+}
+
+} // namespace JSC
Added: trunk/Source/_javascript_Core/heap/HeapProfiler.h (0 => 197489)
--- trunk/Source/_javascript_Core/heap/HeapProfiler.h (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapProfiler.h 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HeapProfiler_h
+#define HeapProfiler_h
+
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class HeapSnapshot;
+class HeapSnapshotBuilder;
+class VM;
+
+class HeapProfiler {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ HeapProfiler(VM&);
+ ~HeapProfiler();
+
+ VM& vm() const { return m_vm; }
+
+ HeapSnapshot* mostRecentSnapshot();
+ void appendSnapshot(std::unique_ptr<HeapSnapshot>);
+ void clearSnapshots();
+
+ HeapSnapshotBuilder* activeSnapshotBuilder() const { return m_activeBuilder; }
+ void setActiveSnapshotBuilder(HeapSnapshotBuilder*);
+
+private:
+ VM& m_vm;
+ Vector<std::unique_ptr<HeapSnapshot>> m_snapshots;
+ HeapSnapshotBuilder* m_activeBuilder { nullptr };
+};
+
+} // namespace JSC
+
+#endif // HeapProfiler_h
Added: trunk/Source/_javascript_Core/heap/HeapSnapshot.cpp (0 => 197489)
--- trunk/Source/_javascript_Core/heap/HeapSnapshot.cpp (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshot.cpp 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HeapSnapshot.h"
+
+#include "JSCInlines.h"
+
+namespace JSC {
+
+HeapSnapshot::HeapSnapshot(HeapSnapshot* previousSnapshot)
+ : m_previous(previousSnapshot)
+{
+}
+
+HeapSnapshot::~HeapSnapshot()
+{
+}
+
+void HeapSnapshot::appendNode(const HeapSnapshotNode& node)
+{
+ ASSERT(!m_finalized);
+ ASSERT(!m_previous || !m_previous->nodeForCell(node.cell));
+
+ m_nodes.append(node);
+}
+
+void HeapSnapshot::sweepCell(JSCell* cell)
+{
+ ASSERT(cell);
+
+ if (m_finalized && !isEmpty()) {
+ unsigned start = 0;
+ unsigned end = m_nodes.size();
+ while (start != end) {
+ unsigned middle = start + ((end - start) / 2);
+ HeapSnapshotNode& node = m_nodes[middle];
+ if (cell == node.cell) {
+ // Cells should always have 0 as low bits.
+ // Mark this cell for removal by setting the low bit.
+ ASSERT(!(reinterpret_cast<intptr_t>(node.cell) & CellToSweepTag));
+ node.cell = reinterpret_cast<JSCell*>(reinterpret_cast<intptr_t>(node.cell) | CellToSweepTag);
+ m_hasCellsToSweep = true;
+ return;
+ }
+ if (cell < node.cell)
+ end = middle;
+ else
+ start = middle + 1;
+ }
+ }
+
+ if (m_previous)
+ m_previous->sweepCell(cell);
+}
+
+void HeapSnapshot::shrinkToFit()
+{
+ if (m_finalized && m_hasCellsToSweep) {
+ m_nodes.removeAllMatching(
+ [&] (const HeapSnapshotNode& node) -> bool {
+ return reinterpret_cast<intptr_t>(node.cell) & CellToSweepTag;
+ });
+ m_nodes.shrinkToFit();
+ m_hasCellsToSweep = false;
+ }
+
+ if (m_previous)
+ m_previous->shrinkToFit();
+}
+
+void HeapSnapshot::finalize()
+{
+ ASSERT(!m_finalized);
+ m_finalized = true;
+
+ // Nodes are appended to the snapshot in identifier order.
+ // Now that we have the complete list of nodes we will sort
+ // them in a different order. Remember the range of identifiers
+ // in this snapshot.
+ if (!isEmpty()) {
+ m_firstObjectIdentifier = m_nodes.first().identifier;
+ m_lastObjectIdentifier = m_nodes.last().identifier;
+ }
+
+ std::sort(m_nodes.begin(), m_nodes.end(), [] (const HeapSnapshotNode& a, const HeapSnapshotNode& b) {
+ return a.cell < b.cell;
+ });
+
+#ifndef NDEBUG
+ // Assert there are no duplicates or nullptr cells.
+ JSCell* previousCell = nullptr;
+ for (auto& node : m_nodes) {
+ ASSERT(node.cell);
+ ASSERT(!(reinterpret_cast<intptr_t>(node.cell) & CellToSweepTag));
+ if (previousCell)
+ ASSERT(node.cell != previousCell);
+ previousCell = node.cell;
+ }
+#endif
+}
+
+Optional<HeapSnapshotNode> HeapSnapshot::nodeForCell(JSCell* cell)
+{
+ ASSERT(m_finalized);
+
+ if (!isEmpty()) {
+ unsigned start = 0;
+ unsigned end = m_nodes.size();
+ while (start != end) {
+ unsigned middle = start + ((end - start) / 2);
+ HeapSnapshotNode& node = m_nodes[middle];
+ if (cell == node.cell)
+ return Optional<HeapSnapshotNode>(node);
+ if (cell < node.cell)
+ end = middle;
+ else
+ start = middle + 1;
+ }
+ }
+
+ if (m_previous)
+ return m_previous->nodeForCell(cell);
+
+ return Nullopt;
+}
+
+Optional<HeapSnapshotNode> HeapSnapshot::nodeForObjectIdentifier(unsigned objectIdentifier)
+{
+ if (isEmpty()) {
+ if (m_previous)
+ return m_previous->nodeForObjectIdentifier(objectIdentifier);
+ return Nullopt;
+ }
+
+ if (objectIdentifier > m_lastObjectIdentifier)
+ return Nullopt;
+
+ if (objectIdentifier < m_firstObjectIdentifier) {
+ if (m_previous)
+ return m_previous->nodeForObjectIdentifier(objectIdentifier);
+ return Nullopt;
+ }
+
+ for (auto& node : m_nodes) {
+ if (node.identifier == objectIdentifier)
+ return Optional<HeapSnapshotNode>(node);
+ }
+
+ return Nullopt;
+}
+
+} // namespace JSC
Added: trunk/Source/_javascript_Core/heap/HeapSnapshot.h (0 => 197489)
--- trunk/Source/_javascript_Core/heap/HeapSnapshot.h (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshot.h 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HeapSnapshot_h
+#define HeapSnapshot_h
+
+#include "HeapSnapshotBuilder.h"
+#include <wtf/Optional.h>
+
+namespace JSC {
+
+class HeapSnapshot {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ HeapSnapshot(HeapSnapshot*);
+ ~HeapSnapshot();
+
+ HeapSnapshot* previous() const { return m_previous; }
+
+ void appendNode(const HeapSnapshotNode&);
+ void sweepCell(JSCell*);
+ void shrinkToFit();
+ void finalize();
+
+ bool isEmpty() const { return m_nodes.isEmpty(); }
+ Optional<HeapSnapshotNode> nodeForCell(JSCell*);
+ Optional<HeapSnapshotNode> nodeForObjectIdentifier(unsigned objectIdentifier);
+
+private:
+ friend class HeapSnapshotBuilder;
+ static const intptr_t CellToSweepTag = 1;
+
+ Vector<HeapSnapshotNode> m_nodes;
+ HeapSnapshot* m_previous { nullptr };
+ unsigned m_firstObjectIdentifier { 0 };
+ unsigned m_lastObjectIdentifier { 0 };
+ bool m_finalized { false };
+ bool m_hasCellsToSweep { false };
+};
+
+} // namespace JSC
+
+#endif // HeapSnapshot_h
Added: trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp (0 => 197489)
--- trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.cpp 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "HeapSnapshotBuilder.h"
+
+#include "DeferGC.h"
+#include "Heap.h"
+#include "HeapProfiler.h"
+#include "HeapSnapshot.h"
+#include "JSCInlines.h"
+#include "JSCell.h"
+#include "VM.h"
+#include <wtf/text/StringBuilder.h>
+
+namespace JSC {
+
+unsigned HeapSnapshotBuilder::nextAvailableObjectIdentifier = 1;
+unsigned HeapSnapshotBuilder::getNextObjectIdentifier() { return nextAvailableObjectIdentifier++; }
+
+HeapSnapshotBuilder::HeapSnapshotBuilder(HeapProfiler& profiler)
+ : m_profiler(profiler)
+{
+}
+
+HeapSnapshotBuilder::~HeapSnapshotBuilder()
+{
+}
+
+void HeapSnapshotBuilder::buildSnapshot()
+{
+ m_snapshot = std::make_unique<HeapSnapshot>(m_profiler.mostRecentSnapshot());
+ {
+ m_profiler.setActiveSnapshotBuilder(this);
+ m_profiler.vm().heap.collectAllGarbage();
+ m_profiler.setActiveSnapshotBuilder(nullptr);
+ }
+ m_snapshot->finalize();
+
+ m_profiler.appendSnapshot(WTFMove(m_snapshot));
+}
+
+void HeapSnapshotBuilder::appendNode(JSCell* cell)
+{
+ ASSERT(m_profiler.activeSnapshotBuilder() == this);
+ ASSERT(Heap::isMarked(cell));
+
+ if (hasExistingNodeForCell(cell))
+ return;
+
+ std::lock_guard<Lock> lock(m_appendingNodeMutex);
+
+ m_snapshot->appendNode(HeapSnapshotNode(cell, getNextObjectIdentifier()));
+}
+
+void HeapSnapshotBuilder::appendEdge(JSCell* from, JSCell* to)
+{
+ ASSERT(m_profiler.activeSnapshotBuilder() == this);
+ ASSERT(to);
+
+ // Avoid trivial edges.
+ if (from == to)
+ return;
+
+ std::lock_guard<Lock> lock(m_appendingEdgeMutex);
+
+ m_edges.append(HeapSnapshotEdge(from, to));
+}
+
+bool HeapSnapshotBuilder::hasExistingNodeForCell(JSCell* cell)
+{
+ if (!m_snapshot->previous())
+ return false;
+
+ return !!m_snapshot->previous()->nodeForCell(cell);
+}
+
+
+// Heap Snapshot JSON Format:
+//
+// {
+// "version": 1.0,
+// "nodes": [
+// [<nodeId>, <sizeInBytes>, <nodeClassNameIndex>, <optionalInternal>], ...
+// ],
+// "nodeClassNames": [
+// "string", "Structure", "Object", ...
+// ],
+// "edges": [
+// [<fromNodeId>, <toNodeId>, <edgeTypeIndex>, <optionalEdgeExtraData>], ...
+// ],
+// "edgeTypes": [
+// "Internal", "Property", "Index", "Variable"
+// ]
+// }
+//
+// FIXME: Possible compaction improvements:
+// - eliminate inner array groups and just have a single list with fixed group sizes (meta data section).
+// - eliminate duplicate edge extra data strings, have an index into a de-duplicated like edgeTypes / nodeClassNames.
+
+static uint8_t edgeTypeToNumber(EdgeType type)
+{
+ return static_cast<uint8_t>(type);
+}
+
+static const char* edgeTypeToString(EdgeType type)
+{
+ switch (type) {
+ case EdgeType::Internal:
+ return "Internal";
+ case EdgeType::Property:
+ return "Property";
+ case EdgeType::Index:
+ return "Index";
+ case EdgeType::Variable:
+ return "Variable";
+ }
+ ASSERT_NOT_REACHED();
+ return "Internal";
+}
+
+String HeapSnapshotBuilder::json()
+{
+ return json([] (const HeapSnapshotNode&) { return true; });
+}
+
+String HeapSnapshotBuilder::json(std::function<bool (const HeapSnapshotNode&)> allowNodeCallback)
+{
+ VM& vm = m_profiler.vm();
+ DeferGCForAWhile deferGC(vm.heap);
+
+ // Build a node to identifier map of allowed nodes to use when serializing edges.
+ HashMap<JSCell*, unsigned> allowedNodeIdentifiers;
+
+ // Build a list of used class names.
+ HashMap<const char*, unsigned> classNameIndexes;
+ unsigned nextClassNameIndex = 0;
+
+ StringBuilder json;
+
+ auto appendNodeJSON = [&] (const HeapSnapshotNode& node) {
+ // Let the client decide if they want to allow or disallow certain nodes.
+ if (!allowNodeCallback(node))
+ return;
+
+ allowedNodeIdentifiers.set(node.cell, node.identifier);
+
+ auto result = classNameIndexes.add(node.cell->classInfo()->className, nextClassNameIndex);
+ if (result.isNewEntry)
+ nextClassNameIndex++;
+ unsigned classNameIndex = result.iterator->value;
+
+ bool isInternal = false;
+ if (!node.cell->isString()) {
+ Structure* structure = node.cell->structure(vm);
+ isInternal = !structure || !structure->globalObject();
+ }
+
+ // [<nodeId>, <sizeInBytes>, <className>, <optionalInternalBoolean>]
+ json.append(',');
+ json.append('[');
+ json.appendNumber(node.identifier);
+ json.append(',');
+ json.appendNumber(node.cell->estimatedSizeInBytes());
+ json.append(',');
+ json.appendNumber(classNameIndex);
+ if (isInternal)
+ json.appendLiteral(",1");
+ json.append(']');
+ };
+
+ bool firstEdge = true;
+ auto appendEdgeJSON = [&] (const HeapSnapshotEdge& edge) {
+ // If the from cell is null, this means a root edge.
+ unsigned fromIdentifier = 0;
+ if (edge.from) {
+ auto fromLookup = allowedNodeIdentifiers.find(edge.from);
+ if (fromLookup == allowedNodeIdentifiers.end())
+ return;
+ fromIdentifier = fromLookup->value;
+ }
+
+ unsigned toIdentifier = 0;
+ if (edge.to) {
+ auto toLookup = allowedNodeIdentifiers.find(edge.to);
+ if (toLookup == allowedNodeIdentifiers.end())
+ return;
+ toIdentifier = toLookup->value;
+ }
+
+ if (!firstEdge)
+ json.append(',');
+ firstEdge = false;
+
+ // [<fromNodeId>, <toNodeId>, <edgeTypeIndex>, <optionalEdgeExtraData>],
+ json.append('[');
+ json.appendNumber(fromIdentifier);
+ json.append(',');
+ json.appendNumber(toIdentifier);
+ json.append(',');
+ json.appendNumber(edgeTypeToNumber(edge.type));
+ json.append(']');
+ };
+
+ json.append('{');
+
+ // version
+ json.appendLiteral("\"version\":1");
+
+ // nodes
+ json.append(',');
+ json.appendLiteral("\"nodes\":");
+ json.append('[');
+ json.appendLiteral("[0,0,\"<root>\"]");
+ for (HeapSnapshot* snapshot = m_profiler.mostRecentSnapshot(); snapshot; snapshot = snapshot->previous()) {
+ for (auto& node : snapshot->m_nodes)
+ appendNodeJSON(node);
+ }
+ json.append(']');
+
+ // node class names
+ json.append(',');
+ json.appendLiteral("\"nodeClassNames\":");
+ json.append('[');
+ Vector<const char *> orderedClassNames(classNameIndexes.size());
+ for (auto& entry : classNameIndexes)
+ orderedClassNames[entry.value] = entry.key;
+ bool firstClassName = true;
+ for (auto& className : orderedClassNames) {
+ if (!firstClassName)
+ json.append(',');
+ firstClassName = false;
+ json.appendQuotedJSONString(className);
+ }
+ json.append(']');
+
+ // edges
+ json.append(',');
+ json.appendLiteral("\"edges\":");
+ json.append('[');
+ for (auto& edge : m_edges)
+ appendEdgeJSON(edge);
+ json.append(']');
+
+ // edge types
+ json.append(',');
+ json.appendLiteral("\"edgeTypes\":");
+ json.append('[');
+ json.appendQuotedJSONString(edgeTypeToString(EdgeType::Internal));
+ json.append(',');
+ json.appendQuotedJSONString(edgeTypeToString(EdgeType::Property));
+ json.append(',');
+ json.appendQuotedJSONString(edgeTypeToString(EdgeType::Index));
+ json.append(',');
+ json.appendQuotedJSONString(edgeTypeToString(EdgeType::Variable));
+ json.append(']');
+
+ json.append('}');
+ return json.toString();
+}
+
+} // namespace JSC
Added: trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h (0 => 197489)
--- trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h (rev 0)
+++ trunk/Source/_javascript_Core/heap/HeapSnapshotBuilder.h 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef HeapSnapshotBuilder_h
+#define HeapSnapshotBuilder_h
+
+#include <functional>
+#include <wtf/Lock.h>
+#include <wtf/Vector.h>
+#include <wtf/text/WTFString.h>
+
+namespace JSC {
+
+class HeapProfiler;
+class HeapSnapshot;
+class JSCell;
+
+struct HeapSnapshotNode {
+ HeapSnapshotNode(JSCell* cell, unsigned identifier)
+ : cell(cell)
+ , identifier(identifier)
+ { }
+
+ JSCell* cell;
+ unsigned identifier;
+};
+
+enum class EdgeType : uint8_t {
+ Internal, // Normal strong reference. No name.
+ Property, // Named property. In `object.property` the name is "property"
+ Index, // Indexed property. In `array[0]` name is index "0".
+ Variable, // Variable held by a scope. In `let x, f=() => x++` name is "x" in f's captured scope.
+ // FIXME: <https://webkit.org/b/154934> Heap Snapshot should include "Weak" edges
+};
+
+struct HeapSnapshotEdge {
+ HeapSnapshotEdge(JSCell* from, JSCell* to)
+ : from(from)
+ , to(to)
+ , type(EdgeType::Internal)
+ { }
+
+ JSCell* from;
+ JSCell* to;
+ EdgeType type;
+};
+
+class JS_EXPORT_PRIVATE HeapSnapshotBuilder {
+ WTF_MAKE_FAST_ALLOCATED;
+public:
+ HeapSnapshotBuilder(HeapProfiler&);
+ ~HeapSnapshotBuilder();
+
+ static unsigned nextAvailableObjectIdentifier;
+ static unsigned getNextObjectIdentifier();
+
+ // Performs a garbage collection that builds a snapshot of all live cells.
+ void buildSnapshot();
+
+ // A marked cell.
+ void appendNode(JSCell*);
+
+ // A reference from one cell to another.
+ void appendEdge(JSCell* from, JSCell* to);
+
+ String json();
+ String json(std::function<bool (const HeapSnapshotNode&)> allowNodeCallback);
+
+private:
+ // Finalized snapshots are not modified during building. So searching them
+ // for an existing node can be done concurrently without a lock.
+ bool hasExistingNodeForCell(JSCell*);
+
+ HeapProfiler& m_profiler;
+
+ // SlotVisitors run in parallel.
+ Lock m_appendingNodeMutex;
+ std::unique_ptr<HeapSnapshot> m_snapshot;
+ Lock m_appendingEdgeMutex;
+ Vector<HeapSnapshotEdge> m_edges;
+};
+
+} // namespace JSC
+
+#endif // HeapSnapshotBuilder_h
Modified: trunk/Source/_javascript_Core/heap/SlotVisitor.cpp (197488 => 197489)
--- trunk/Source/_javascript_Core/heap/SlotVisitor.cpp 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/heap/SlotVisitor.cpp 2016-03-03 05:15:56 UTC (rev 197489)
@@ -31,6 +31,8 @@
#include "CopiedBlockInlines.h"
#include "CopiedSpace.h"
#include "CopiedSpaceInlines.h"
+#include "HeapProfiler.h"
+#include "HeapSnapshotBuilder.h"
#include "JSArray.h"
#include "JSDestructibleObject.h"
#include "VM.h"
@@ -94,6 +96,9 @@
{
if (heap()->operationInProgress() == FullCollection)
ASSERT(m_opaqueRoots.isEmpty()); // Should have merged by now.
+
+ if (HeapProfiler* heapProfiler = vm().heapProfiler())
+ m_heapSnapshotBuilder = heapProfiler->activeSnapshotBuilder();
}
void SlotVisitor::reset()
@@ -101,6 +106,8 @@
m_bytesVisited = 0;
m_bytesCopied = 0;
m_visitCount = 0;
+ m_heapSnapshotBuilder = nullptr;
+ ASSERT(!m_currentCell);
ASSERT(m_stack.isEmpty());
}
@@ -121,6 +128,10 @@
{
if (!value || !value.isCell())
return;
+
+ if (m_heapSnapshotBuilder)
+ m_heapSnapshotBuilder->appendEdge(m_currentCell, value.asCell());
+
setMarkedAndAppendToMarkStack(value.asCell());
}
@@ -155,12 +166,37 @@
m_visitCount++;
m_bytesVisited += MarkedBlock::blockFor(cell)->cellSize();
m_stack.append(cell);
+
+ if (m_heapSnapshotBuilder)
+ m_heapSnapshotBuilder->appendNode(cell);
}
+class SetCurrentCellScope {
+public:
+ SetCurrentCellScope(SlotVisitor& visitor, const JSCell* cell)
+ : m_visitor(visitor)
+ {
+ ASSERT(!m_visitor.m_currentCell);
+ m_visitor.m_currentCell = const_cast<JSCell*>(cell);
+ }
+
+ ~SetCurrentCellScope()
+ {
+ ASSERT(m_visitor.m_currentCell);
+ m_visitor.m_currentCell = nullptr;
+ }
+
+private:
+ SlotVisitor& m_visitor;
+};
+
+
ALWAYS_INLINE void SlotVisitor::visitChildren(const JSCell* cell)
{
ASSERT(Heap::isMarked(cell));
+ SetCurrentCellScope currentCellScope(*this, cell);
+
m_currentObjectCellStateBeforeVisiting = cell->cellState();
cell->setCellState(CellState::OldBlack);
Modified: trunk/Source/_javascript_Core/heap/SlotVisitor.h (197488 => 197489)
--- trunk/Source/_javascript_Core/heap/SlotVisitor.h 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/heap/SlotVisitor.h 2016-03-03 05:15:56 UTC (rev 197489)
@@ -37,6 +37,7 @@
class ConservativeRoots;
class GCThreadSharedData;
class Heap;
+class HeapSnapshotBuilder;
template<typename T> class JITWriteBarrier;
class UnconditionalFinalizer;
template<typename T> class Weak;
@@ -47,6 +48,7 @@
WTF_MAKE_NONCOPYABLE(SlotVisitor);
WTF_MAKE_FAST_ALLOCATED;
+ friend class SetCurrentCellScope;
friend class HeapRootVisitor; // Allowed to mark a JSValue* or JSCell** directly.
friend class Heap;
@@ -111,6 +113,8 @@
void dump(PrintStream&) const;
+ bool isBuildingHeapSnapshot() const { return !!m_heapSnapshotBuilder; }
+
private:
friend class ParallelModeEnabler;
@@ -137,6 +141,9 @@
Heap& m_heap;
+ HeapSnapshotBuilder* m_heapSnapshotBuilder { nullptr };
+ JSCell* m_currentCell { nullptr };
+
CellState m_currentObjectCellStateBeforeVisiting { CellState::NewWhite };
public:
Modified: trunk/Source/_javascript_Core/jsc.cpp (197488 => 197489)
--- trunk/Source/_javascript_Core/jsc.cpp 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/jsc.cpp 2016-03-03 05:15:56 UTC (rev 197489)
@@ -32,6 +32,8 @@
#include "Disassembler.h"
#include "Exception.h"
#include "ExceptionHelpers.h"
+#include "HeapProfiler.h"
+#include "HeapSnapshotBuilder.h"
#include "HeapStatistics.h"
#include "InitializeThreading.h"
#include "Interpreter.h"
@@ -465,12 +467,67 @@
Vector<int> m_vector;
};
+class SimpleObject : public JSNonFinalObject {
+public:
+ SimpleObject(VM& vm, Structure* structure)
+ : Base(vm, structure)
+ {
+ }
+
+ typedef JSNonFinalObject Base;
+ static const bool needsDestruction = false;
+
+ static SimpleObject* create(VM& vm, JSGlobalObject* globalObject)
+ {
+ Structure* structure = createStructure(vm, globalObject, jsNull());
+ SimpleObject* simpleObject = new (NotNull, allocateCell<SimpleObject>(vm.heap, sizeof(SimpleObject))) SimpleObject(vm, structure);
+ simpleObject->finishCreation(vm);
+ return simpleObject;
+ }
+
+ void finishCreation(VM& vm)
+ {
+ Base::finishCreation(vm);
+ }
+
+ static void visitChildren(JSCell* cell, SlotVisitor& visitor)
+ {
+ SimpleObject* thisObject = jsCast<SimpleObject*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+ Base::visitChildren(thisObject, visitor);
+ visitor.append(&thisObject->m_hiddenValue);
+ }
+
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+ }
+
+ JSValue hiddenValue()
+ {
+ return m_hiddenValue.get();
+ }
+
+ void setHiddenValue(VM& vm, JSValue value)
+ {
+ ASSERT(value.isCell());
+ m_hiddenValue.set(vm, this, value);
+ }
+
+ DECLARE_INFO;
+
+private:
+ WriteBarrier<Unknown> m_hiddenValue;
+};
+
+
const ClassInfo Element::s_info = { "Element", &Base::s_info, 0, CREATE_METHOD_TABLE(Element) };
const ClassInfo Masquerader::s_info = { "Masquerader", &Base::s_info, 0, CREATE_METHOD_TABLE(Masquerader) };
const ClassInfo Root::s_info = { "Root", &Base::s_info, 0, CREATE_METHOD_TABLE(Root) };
const ClassInfo ImpureGetter::s_info = { "ImpureGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(ImpureGetter) };
const ClassInfo CustomGetter::s_info = { "CustomGetter", &Base::s_info, 0, CREATE_METHOD_TABLE(CustomGetter) };
const ClassInfo RuntimeArray::s_info = { "RuntimeArray", &Base::s_info, 0, CREATE_METHOD_TABLE(RuntimeArray) };
+const ClassInfo SimpleObject::s_info = { "SimpleObject", &Base::s_info, 0, CREATE_METHOD_TABLE(SimpleObject) };
ElementHandleOwner* Element::handleOwner()
{
@@ -501,6 +558,9 @@
static EncodedJSValue JSC_HOST_CALL functionCreateRoot(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionCreateElement(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionGetElement(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionCreateSimpleObject(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionGetHiddenValue(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionSetHiddenValue(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
@@ -555,6 +615,7 @@
static EncodedJSValue JSC_HOST_CALL functionLoadModule(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionCheckModuleSyntax(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionPlatformSupportsSamplingProfiler(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshot(ExecState*);
#if ENABLE(SAMPLING_PROFILER)
static EncodedJSValue JSC_HOST_CALL functionStartSamplingProfiler(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionSamplingProfilerStackTraces(ExecState*);
@@ -708,6 +769,10 @@
addFunction(vm, "getElement", functionGetElement, 1);
addFunction(vm, "setElementRoot", functionSetElementRoot, 2);
+ addConstructableFunction(vm, "SimpleObject", functionCreateSimpleObject, 0);
+ addFunction(vm, "getHiddenValue", functionGetHiddenValue, 1);
+ addFunction(vm, "setHiddenValue", functionSetHiddenValue, 2);
+
putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "DFGTrue"), 0, functionFalse1, DFGTrueIntrinsic, DontEnum);
putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "OSRExit"), 0, functionUndefined1, OSRExitIntrinsic, DontEnum);
putDirectNativeFunction(vm, this, Identifier::fromString(&vm, "isFinalTier"), 0, functionFalse2, IsFinalTierIntrinsic, DontEnum);
@@ -747,6 +812,7 @@
addFunction(vm, "checkModuleSyntax", functionCheckModuleSyntax, 1);
addFunction(vm, "platformSupportsSamplingProfiler", functionPlatformSupportsSamplingProfiler, 0);
+ addFunction(vm, "generateHeapSnapshot", functionGenerateHeapSnapshot, 0);
#if ENABLE(SAMPLING_PROFILER)
addFunction(vm, "startSamplingProfiler", functionStartSamplingProfiler, 0);
addFunction(vm, "samplingProfilerStackTraces", functionSamplingProfilerStackTraces, 0);
@@ -1133,6 +1199,28 @@
return JSValue::encode(jsUndefined());
}
+EncodedJSValue JSC_HOST_CALL functionCreateSimpleObject(ExecState* exec)
+{
+ JSLockHolder lock(exec);
+ return JSValue::encode(SimpleObject::create(exec->vm(), exec->lexicalGlobalObject()));
+}
+
+EncodedJSValue JSC_HOST_CALL functionGetHiddenValue(ExecState* exec)
+{
+ JSLockHolder lock(exec);
+ SimpleObject* simpleObject = jsCast<SimpleObject*>(exec->argument(0).asCell());
+ return JSValue::encode(simpleObject->hiddenValue());
+}
+
+EncodedJSValue JSC_HOST_CALL functionSetHiddenValue(ExecState* exec)
+{
+ JSLockHolder lock(exec);
+ SimpleObject* simpleObject = jsCast<SimpleObject*>(exec->argument(0).asCell());
+ JSValue value = exec->argument(1);
+ simpleObject->setHiddenValue(exec->vm(), value);
+ return JSValue::encode(jsUndefined());
+}
+
EncodedJSValue JSC_HOST_CALL functionCreateProxy(ExecState* exec)
{
JSLockHolder lock(exec);
@@ -1646,6 +1734,19 @@
#endif
}
+EncodedJSValue JSC_HOST_CALL functionGenerateHeapSnapshot(ExecState* exec)
+{
+ JSLockHolder lock(exec);
+
+ HeapSnapshotBuilder snapshotBuilder(exec->vm().ensureHeapProfiler());
+ snapshotBuilder.buildSnapshot();
+
+ String jsonString = snapshotBuilder.json();
+ EncodedJSValue result = JSValue::encode(JSONParse(exec, jsonString));
+ RELEASE_ASSERT(!exec->hadException());
+ return result;
+}
+
#if ENABLE(SAMPLING_PROFILER)
EncodedJSValue JSC_HOST_CALL functionStartSamplingProfiler(ExecState* exec)
{
Modified: trunk/Source/_javascript_Core/runtime/VM.cpp (197488 => 197489)
--- trunk/Source/_javascript_Core/runtime/VM.cpp 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/runtime/VM.cpp 2016-03-03 05:15:56 UTC (rev 197489)
@@ -50,6 +50,7 @@
#include "GetterSetter.h"
#include "Heap.h"
#include "HeapIterationScope.h"
+#include "HeapProfiler.h"
#include "HostCallReturnValue.h"
#include "Identifier.h"
#include "IncrementalSweeper.h"
@@ -443,6 +444,13 @@
return *m_watchdog;
}
+HeapProfiler& VM::ensureHeapProfiler()
+{
+ if (!m_heapProfiler)
+ m_heapProfiler = std::make_unique<HeapProfiler>(*this);
+ return *m_heapProfiler;
+}
+
#if ENABLE(SAMPLING_PROFILER)
void VM::ensureSamplingProfiler(RefPtr<Stopwatch>&& stopwatch)
{
Modified: trunk/Source/_javascript_Core/runtime/VM.h (197488 => 197489)
--- trunk/Source/_javascript_Core/runtime/VM.h 2016-03-03 03:50:37 UTC (rev 197488)
+++ trunk/Source/_javascript_Core/runtime/VM.h 2016-03-03 05:15:56 UTC (rev 197489)
@@ -87,6 +87,7 @@
class HandleStack;
class TypeProfiler;
class TypeProfilerLog;
+class HeapProfiler;
class Identifier;
class Interpreter;
class JSBoundSlotBaseFunction;
@@ -249,6 +250,9 @@
JS_EXPORT_PRIVATE Watchdog& ensureWatchdog();
JS_EXPORT_PRIVATE Watchdog* watchdog() { return m_watchdog.get(); }
+ JS_EXPORT_PRIVATE HeapProfiler* heapProfiler() const { return m_heapProfiler.get(); }
+ JS_EXPORT_PRIVATE HeapProfiler& ensureHeapProfiler();
+
#if ENABLE(SAMPLING_PROFILER)
JS_EXPORT_PRIVATE SamplingProfiler* samplingProfiler() { return m_samplingProfiler.get(); }
JS_EXPORT_PRIVATE void ensureSamplingProfiler(RefPtr<Stopwatch>&&);
@@ -672,6 +676,7 @@
Deque<std::unique_ptr<QueuedTask>> m_microtaskQueue;
MallocPtr<EncodedJSValue> m_exceptionFuzzBuffer;
RefPtr<Watchdog> m_watchdog;
+ std::unique_ptr<HeapProfiler> m_heapProfiler;
#if ENABLE(SAMPLING_PROFILER)
RefPtr<SamplingProfiler> m_samplingProfiler;
#endif
Added: trunk/Source/_javascript_Core/tests/heapProfiler/basic-edges.js (0 => 197489)
--- trunk/Source/_javascript_Core/tests/heapProfiler/basic-edges.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/heapProfiler/basic-edges.js 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,58 @@
+load("./driver/driver.js");
+
+function excludeStructure(edges) {
+ return edges.filter((x) => x.to.className !== "Structure");
+}
+
+let simpleObject1NodeId;
+let simpleObject2NodeId;
+
+let simpleObject1 = new SimpleObject;
+let simpleObject2 = new SimpleObject;
+
+(function() {
+ let snapshot = createCheapHeapSnapshot();
+ let nodes = snapshot.nodesWithClassName("SimpleObject");
+ assert(nodes.length === 2, "Snapshot should contain 2 'SimpleObject' instances");
+ let simpleObject1Node = nodes[0].outgoingEdges.length === 2 ? nodes[0] : nodes[1];
+ let simpleObject2Node = nodes[0].outgoingEdges.length === 1 ? nodes[0] : nodes[1];
+ assert(simpleObject1Node.outgoingEdges.length === 1, "'simpleObject1' should reference only its structure");
+ assert(simpleObject2Node.outgoingEdges.length === 1, "'simpleObject2' should reference only its structure");
+})();
+
+setHiddenValue(simpleObject1, simpleObject2);
+
+(function() {
+ let snapshot = createCheapHeapSnapshot();
+ let nodes = snapshot.nodesWithClassName("SimpleObject");
+ assert(nodes.length === 2, "Snapshot should contain 2 'SimpleObject' instances");
+ let simpleObject1Node = nodes[0].outgoingEdges.length === 2 ? nodes[0] : nodes[1];
+ let simpleObject2Node = nodes[0].outgoingEdges.length === 1 ? nodes[0] : nodes[1];
+ assert(simpleObject1Node.outgoingEdges.length === 2, "'simpleObject1' should reference its structure and hidden value");
+ assert(simpleObject2Node.outgoingEdges.length === 1, "'simpleObject2' should reference only its structure");
+ assert(excludeStructure(simpleObject1Node.outgoingEdges)[0].to.id === simpleObject2Node.id, "'simpleObject1' should reference 'simpleObject2'");
+ simpleObject1NodeId = simpleObject1Node.id;
+ simpleObject2NodeId = simpleObject2Node.id;
+})();
+
+simpleObject2 = null;
+
+(function() {
+ let snapshot = createCheapHeapSnapshot();
+ let nodes = snapshot.nodesWithClassName("SimpleObject");
+ assert(nodes.length === 2, "Snapshot should contain 2 'SimpleObject' instances");
+ let simpleObject1Node = nodes[0].id === simpleObject1NodeId ? nodes[0] : nodes[1];
+ let simpleObject2Node = nodes[0].id === simpleObject2NodeId ? nodes[0] : nodes[1];
+ assert(simpleObject1Node.id === simpleObject1NodeId && simpleObject2Node.id === simpleObject2NodeId, "node identifiers were maintained");
+ assert(simpleObject1Node.outgoingEdges.length === 2, "'simpleObject1' should reference its structure and hidden value");
+ assert(simpleObject2Node.outgoingEdges.length === 1, "'simpleObject2' should reference only its structure");
+ assert(excludeStructure(simpleObject1Node.outgoingEdges)[0].to.id === simpleObject2NodeId, "'simpleObject1' should reference 'simpleObject2'");
+})();
+
+simpleObject1 = null;
+
+(function() {
+ let snapshot = createCheapHeapSnapshot();
+ let nodes = snapshot.nodesWithClassName("SimpleObject");
+ assert(nodes.length === 0, "Snapshot should not contain a 'SimpleObject' instance");
+})();
Added: trunk/Source/_javascript_Core/tests/heapProfiler/basic-nodes.js (0 => 197489)
--- trunk/Source/_javascript_Core/tests/heapProfiler/basic-nodes.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/heapProfiler/basic-nodes.js 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,78 @@
+load("./driver/driver.js");
+
+function hasDifferentSizeNodes(nodes) {
+ let seenSize = nodes[0].size;
+ for (let node of nodes) {
+ if (node.size !== seenSize)
+ return true;
+ }
+ return false;
+}
+
+function hasAllInternalNodes(nodes) {
+ for (let node of nodes) {
+ if (!node.internal)
+ return false;
+ }
+ return true;
+}
+
+function sorted(nodes) {
+ return nodes.sort((a, b) => a.id - b.id);
+}
+
+let simpleObject1NodeId;
+let simpleObject2NodeId;
+
+(function() {
+ let snapshot = createCheapHeapSnapshot();
+ assert(snapshot.nodesWithClassName("global").length === 1, "Snapshot should contain a single 'global' node");
+ assert(snapshot.nodesWithClassName("Structure").length > 0, "Snapshot should contain 'Structure' nodes");
+ assert(snapshot.nodesWithClassName("ThisClassNameDoesNotExist").length === 0, "Snapshot should not contain 'ThisClassNameDoesNotExist' nodes");
+
+ let strings = snapshot.nodesWithClassName("string");
+ assert(strings.length > 0, "Snapshot should contain 'string' nodes");
+ assert(hasDifferentSizeNodes(strings), "'string' nodes should have different sizes");
+
+ let nativeExecutables = snapshot.nodesWithClassName("NativeExecutable");
+ assert(nativeExecutables.length > 0, "Snapshot should contain 'NativeExecutable' nodes");
+ assert(!hasDifferentSizeNodes(nativeExecutables), "'NativeExecutable' nodes should all be the same size");
+ assert(hasAllInternalNodes(nativeExecutables), "'NativeExecutable' nodes should all be internal");
+
+ assert(snapshot.nodesWithClassName("SimpleObject").length === 0, "Snapshot should not contain a 'SimpleObject' instance");
+})();
+
+let simpleObject1 = new SimpleObject;
+
+(function() {
+ let snapshot = createCheapHeapSnapshot();
+ let nodes = sorted(snapshot.nodesWithClassName("SimpleObject"));
+ let [simpleObject1Node] = nodes;
+ simpleObject1NodeId = nodes[0].id;
+ assert(nodes.length === 1, "Snapshot should contain 1 'SimpleObject' instance");
+ assert(simpleObject1Node.outgoingEdges.length === 1, "'simpleObject1' should only reference its structure");
+ assert(simpleObject1Node.outgoingEdges[0].to.className === "Structure", "'simpleObject1' should reference a Structure");
+})();
+
+let simpleObjectList = [];
+for (let i = 0; i < 1234; ++i)
+ simpleObjectList.push(new SimpleObject);
+
+(function() {
+ let snapshot = createCheapHeapSnapshot();
+ let nodes = sorted(snapshot.nodesWithClassName("SimpleObject"));
+ simpleObject1NodeId = nodes[0].id;
+ simpleObject2NodeId = nodes[1].id;
+ assert(nodes.length === 1235, "Snapshot should contain 1235 'SimpleObject' instances");
+ assert(nodes[0].id === simpleObject1NodeId, "'simpleObject1' should maintain the same identifier");
+ assert(simpleObject1NodeId < simpleObject2NodeId, "node identifiers should always increase across snapshots");
+})();
+
+simpleObject1 = null;
+simpleObjectList.fill(null);
+
+(function() {
+ let snapshot = createCheapHeapSnapshot();
+ let nodes = snapshot.nodesWithClassName("SimpleObject");
+ assert(nodes.length === 0, "Snapshot should not contain a 'SimpleObject' instance");
+})();
Added: trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js (0 => 197489)
--- trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/heapProfiler/driver/driver.js 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,242 @@
+function assert(condition, reason) {
+ if (!condition)
+ throw new Error(reason);
+}
+
+// -----------------
+// CheapHeapSnapshot
+//
+// Contains two large lists of all node data and all edge data.
+// Lazily creates node and edge objects off of indexes into these lists.
+
+// [<0:id>, <1:size>, <2:classNameTableIndex>, <3:internal>, <4:firstEdgeIndex>];
+const nodeFieldCount = 5;
+const nodeIdOffset = 0;
+const nodeSizeOffset = 1;
+const nodeClassNameOffset = 2;
+const nodeInternalOffset = 3;
+const nodeFirstEdgeOffset = 4;
+const nodeNoEdgeValue = 0xffffffff; // UINT_MAX
+
+// [<0:from-id>, <1:to-id>, <2:typeTableIndex>, <3:data>]
+const edgeFieldCount = 4;
+const edgeFromIdOffset = 0;
+const edgeToIdOffset = 1;
+const edgeTypeOffset = 2;
+const edgeDataOffset = 3;
+
+CheapHeapSnapshotNode = class CheapHeapSnapshotNode
+{
+ constructor(snapshot, nodeIndex)
+ {
+ assert((nodeIndex % nodeFieldCount) === 0, "Bad Node Index: " + nodeIndex);
+
+ let nodes = snapshot.nodes;
+ this.id = nodes[nodeIndex + nodeIdOffset];
+ this.size = nodes[nodeIndex + nodeSizeOffset];
+ this.className = snapshot.classNameFromTableIndex(nodes[nodeIndex + nodeClassNameOffset]);
+ this.internal = nodes[nodeIndex + nodeInternalOffset] ? true : false;
+
+ this.outgoingEdges = [];
+ let firstEdgeIndex = nodes[nodeIndex + nodeFirstEdgeOffset];
+ if (firstEdgeIndex !== nodeNoEdgeValue) {
+ for (let i = firstEdgeIndex; i < snapshot.edges.length; i += edgeFieldCount) {
+ if (snapshot.edges[i + edgeFromIdOffset] !== this.id)
+ break;
+ this.outgoingEdges.push(new CheapHeapSnapshotEdge(snapshot, i));
+ }
+ }
+ }
+}
+
+CheapHeapSnapshotEdge = class CheapHeapSnapshotEdge
+{
+ constructor(snapshot, edgeIndex)
+ {
+ assert((edgeIndex % edgeFieldCount) === 0, "Bad Edge Index: " + edgeIndex);
+ this.snapshot = snapshot;
+
+ let edges = snapshot.edges;
+ this.fromId = edges[edgeIndex + edgeFromIdOffset];
+ this.toId = edges[edgeIndex + edgeToIdOffset];
+ this.type = snapshot.edgeTypeFromTableIndex(edges[edgeIndex + edgeTypeOffset]);
+ this.data = "" + edgeDataOffset];
+ }
+
+ get from() { return this.snapshot.nodeWithIdentifier(this.fromId); }
+ get to() { return this.snapshot.nodeWithIdentifier(this.toId); }
+}
+
+CheapHeapSnapshot = class CheapHeapSnapshot
+{
+ constructor(json)
+ {
+ let {nodes, nodeClassNames, edges, edgeTypes} = json;
+
+ this._nodes = new Uint32Array(nodes.length * nodeFieldCount);
+ this._edges = new Uint32Array(edges.length * edgeFieldCount);
+ this._nodeIdentifierToIndex = new Map; // <id> => index in _nodes
+
+ this._edgeTypesTable = edgeTypes;
+ this._nodeClassNamesTable = nodeClassNames;
+
+ let n = 0;
+ nodes.forEach((nodePayload) => {
+ let [id, size, classNameTableIndex, internal] = nodePayload;
+ this._nodeIdentifierToIndex.set(id, n);
+ this._nodes[n++] = id;
+ this._nodes[n++] = size;
+ this._nodes[n++] = classNameTableIndex;
+ this._nodes[n++] = internal;
+ this._nodes[n++] = nodeNoEdgeValue;
+ });
+
+ let e = 0;
+ let lastNodeIdentifier = -1;
+ edges.sort((a, b) => a[0] - b[0]).forEach((edgePayload) => {
+ let [fromIdentifier, toIdentifier, edgeTypeTableIndex, data] = edgePayload;
+ if (fromIdentifier !== lastNodeIdentifier) {
+ let nodeIndex = this._nodeIdentifierToIndex.get(fromIdentifier);
+ assert(this._nodes[nodeIndex + nodeIdOffset] === fromIdentifier, "Node lookup failed");
+ this._nodes[nodeIndex + nodeFirstEdgeOffset] = e;
+ lastNodeIdentifier = fromIdentifier;
+ }
+ this._edges[e++] = fromIdentifier;
+ this._edges[e++] = toIdentifier;
+ this._edges[e++] = edgeTypeTableIndex;
+ this._edges[e++] = data;
+ });
+ }
+
+ get nodes() { return this._nodes; }
+ get edges() { return this._edges; }
+
+ nodeWithIdentifier(id)
+ {
+ return new CheapHeapSnapshotNode(this, this._nodeIdentifierToIndex.get(id));
+ }
+
+ nodesWithClassName(className)
+ {
+ let result = [];
+ for (let i = 0; i < this._nodes.length; i += nodeFieldCount) {
+ let classNameTableIndex = this._nodes[i + nodeClassNameOffset];
+ if (this.classNameFromTableIndex(classNameTableIndex) === className)
+ result.push(new CheapHeapSnapshotNode(this, i));
+ }
+ return result;
+ }
+
+ classNameFromTableIndex(tableIndex)
+ {
+ return this._nodeClassNamesTable[tableIndex];
+ }
+
+ edgeTypeFromTableIndex(tableIndex)
+ {
+ return this._edgeTypesTable[tableIndex];
+ }
+}
+
+function createCheapHeapSnapshot() {
+ let json = generateHeapSnapshot();
+
+ let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
+ assert(version === 1, "Heap Snapshot payload should be version 1");
+ assert(nodes.length, "Heap Snapshot should have nodes");
+ assert(nodeClassNames.length, "Heap Snapshot should have nodeClassNames");
+ assert(edges.length, "Heap Snapshot should have edges");
+ assert(edgeTypes.length, "Heap Snapshot should have edgeTypes");
+
+ return new CheapHeapSnapshot(json);
+}
+
+
+// ------------
+// HeapSnapshot
+//
+// This creates a lot of objects that make it easy to walk the entire node graph
+// (incoming and outgoing edges). However when a test creates multiple snapshots
+// with snapshots in scope this can quickly explode into a snapshot with a massive
+// number of nodes/edges. For such cases create CheapHeapSnapshots, which create
+// a very small number of objects per Heap Snapshot.
+
+HeapSnapshotNode = class HeapSnapshotNode
+{
+ constructor(id, className, size, internal)
+ {
+ this.id = id;
+ this.className = className;
+ this.size = size;
+ this.internal = internal;
+ this.incomingEdges = [];
+ this.outgoingEdges = [];
+ }
+}
+
+HeapSnapshotEdge = class HeapSnapshotEdge
+{
+ constructor(from, to, type, data)
+ {
+ this.from = from;
+ this.to = to;
+ this.type = type;
+ this.data = ""
+ }
+}
+
+HeapSnapshot = class HeapSnapshot
+{
+ constructor(json)
+ {
+ let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
+
+ this.nodeMap = new Map;
+
+ this.nodes = nodes.map((nodePayload) => {
+ let [id, size, classNameIndex, internal] = nodePayload;
+ let node = new HeapSnapshotNode(id, nodeClassNames[classNameIndex], size, internal);
+ this.nodeMap.set(id, node);
+ return node;
+ });
+
+ edges.map((edgePayload) => {
+ let [fromIdentifier, toIdentifier, edgeTypeIndex, data] = edgePayload;
+ let from = this.nodeMap.get(fromIdentifier);
+ let to = this.nodeMap.get(toIdentifier);
+ assert(from, "Missing node for `from` part of edge");
+ assert(to, "Missing node for `to` part of edge");
+ let edge = new HeapSnapshotEdge(from, to, edgeTypes[edgeTypeIndex], data);
+ from.outgoingEdges.push(edge);
+ to.incomingEdges.push(edge);
+ });
+
+ this.rootNode = this.nodeMap.get(0);
+ assert(this.rootNode, "Missing <root> node with identifier 0");
+ assert(this.rootNode.outgoingEdges.length > 0, "<root> should have children");
+ assert(this.rootNode.incomingEdges.length === 0, "<root> should not have back references");
+ }
+
+ nodesWithClassName(className)
+ {
+ let result = [];
+ for (let node of this.nodes) {
+ if (node.className === className)
+ result.push(node);
+ }
+ return result;
+ }
+}
+
+function createHeapSnapshot() {
+ let json = generateHeapSnapshot();
+
+ let {version, nodes, nodeClassNames, edges, edgeTypes} = json;
+ assert(version === 1, "Heap Snapshot payload should be version 1");
+ assert(nodes.length, "Heap Snapshot should have nodes");
+ assert(nodeClassNames.length, "Heap Snapshot should have nodeClassNames");
+ assert(edges.length, "Heap Snapshot should have edges");
+ assert(edgeTypes.length, "Heap Snapshot should have edgeTypes");
+
+ return new HeapSnapshot(json);
+}
Added: trunk/Source/_javascript_Core/tests/heapProfiler.yaml (0 => 197489)
--- trunk/Source/_javascript_Core/tests/heapProfiler.yaml (rev 0)
+++ trunk/Source/_javascript_Core/tests/heapProfiler.yaml 2016-03-03 05:15:56 UTC (rev 197489)
@@ -0,0 +1,25 @@
+# Copyright (C) 2016 Apple Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+- path: heapProfiler
+ cmd: defaultRun