Web Inspector: Native Memory Instrumentation: reportLeaf method doesn't report the...
authorloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Feb 2013 09:54:33 +0000 (09:54 +0000)
committerloislo@chromium.org <loislo@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 Feb 2013 09:54:33 +0000 (09:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=109554

In some cases leaves have no pointer so with the old schema we can't generate nodeId for them because we
can't insert 0 into hashmap. It happens when we call addPrivateBuffer method.

Drive by fix: I introduced a client interface for the HeapGraphSerializer.
It helps me to do the tests for the serializer.

Reviewed by Yury Semikhatsky.

It is covered by newly added tests in TestWebKitAPI.

Source/WebCore:

* inspector/HeapGraphSerializer.cpp:
(WebCore::HeapGraphSerializer::HeapGraphSerializer):
(WebCore::HeapGraphSerializer::pushUpdate):
(WebCore::HeapGraphSerializer::reportNode):
(WebCore::HeapGraphSerializer::toNodeId):
(WebCore::HeapGraphSerializer::addRootNode):
* inspector/HeapGraphSerializer.h:
(HeapGraphSerializer):
(Client):
(WebCore::HeapGraphSerializer::Client::~Client):
* inspector/InspectorMemoryAgent.cpp:
(WebCore):
(WebCore::InspectorMemoryAgent::getProcessMemoryDistributionImpl):

Tools:

* TestWebKitAPI/TestWebKitAPI.gypi:
* TestWebKitAPI/Tests/WebCore/HeapGraphSerializerTest.cpp: Added.
(TestWebKitAPI):
(HeapGraphReceiver):
(TestWebKitAPI::HeapGraphReceiver::HeapGraphReceiver):
(TestWebKitAPI::HeapGraphReceiver::printGraph):
(TestWebKitAPI::HeapGraphReceiver::dumpNodes):
(TestWebKitAPI::HeapGraphReceiver::dumpEdges):
(TestWebKitAPI::HeapGraphReceiver::dumpBaseToRealNodeId):
(TestWebKitAPI::HeapGraphReceiver::dumpStrings):
(TestWebKitAPI::HeapGraphReceiver::serializer):
(TestWebKitAPI::HeapGraphReceiver::chunkPart):
(TestWebKitAPI::HeapGraphReceiver::dumpPart):
(TestWebKitAPI::HeapGraphReceiver::stringValue):
(TestWebKitAPI::HeapGraphReceiver::intValue):
(TestWebKitAPI::HeapGraphReceiver::nodeToString):
(TestWebKitAPI::HeapGraphReceiver::edgeToString):
(TestWebKitAPI::HeapGraphReceiver::printNode):
(Helper):
(TestWebKitAPI::Helper::Helper):
(TestWebKitAPI::Helper::addNode):
(TestWebKitAPI::Helper::addEdge):
(TestWebKitAPI::Helper::done):
(Object):
(TestWebKitAPI::Helper::Object::Object):
(TestWebKitAPI::TEST):
(Owner):
(TestWebKitAPI::Owner::Owner):
(TestWebKitAPI::Owner::reportMemoryUsage):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@143175 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/inspector/HeapGraphSerializer.cpp
Source/WebCore/inspector/HeapGraphSerializer.h
Source/WebCore/inspector/InspectorMemoryAgent.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.gyp/TestWebKitAPI.gyp
Tools/TestWebKitAPI/Tests/WebCore/HeapGraphSerializerTest.cpp [new file with mode: 0644]

index f0b2588417cb89c880ceed262fcf65890d7e17e4..5a3d6bbb8350f11f3839ecb44cdd0dfac1bb6981 100644 (file)
@@ -1,3 +1,32 @@
+2013-02-13  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Web Inspector: Native Memory Instrumentation: reportLeaf method doesn't report the leaf node properly.
+        https://bugs.webkit.org/show_bug.cgi?id=109554
+
+        In some cases leaves have no pointer so with the old schema we can't generate nodeId for them because we
+        can't insert 0 into hashmap. It happens when we call addPrivateBuffer method.
+
+        Drive by fix: I introduced a client interface for the HeapGraphSerializer.
+        It helps me to do the tests for the serializer.
+
+        Reviewed by Yury Semikhatsky.
+
+        It is covered by newly added tests in TestWebKitAPI.
+
+        * inspector/HeapGraphSerializer.cpp:
+        (WebCore::HeapGraphSerializer::HeapGraphSerializer):
+        (WebCore::HeapGraphSerializer::pushUpdate):
+        (WebCore::HeapGraphSerializer::reportNode):
+        (WebCore::HeapGraphSerializer::toNodeId):
+        (WebCore::HeapGraphSerializer::addRootNode):
+        * inspector/HeapGraphSerializer.h:
+        (HeapGraphSerializer):
+        (Client):
+        (WebCore::HeapGraphSerializer::Client::~Client):
+        * inspector/InspectorMemoryAgent.cpp:
+        (WebCore):
+        (WebCore::InspectorMemoryAgent::getProcessMemoryDistributionImpl):
+
 2013-02-18  Sheriff Bot  <webkit.review.bot@gmail.com>
 
         Unreviewed, rolling out r143100.
index 1479517b3362b9e3ebd8cc6eb60e602119e75a45..dbe2bdc171ea5ff7f7daa02100bea49096b29cde 100644 (file)
 
 namespace WebCore {
 
-HeapGraphSerializer::HeapGraphSerializer(InspectorFrontend::Memory* frontend)
-    : m_frontend(frontend)
+HeapGraphSerializer::HeapGraphSerializer(Client* client)
+    : m_client(client)
     , m_strings(Strings::create())
     , m_edges(Edges::create())
     , m_nodeEdgesCount(0)
     , m_nodes(Nodes::create())
     , m_baseToRealNodeIdMap(BaseToRealNodeIdMap::create())
+    , m_leafCount(0)
 {
-    ASSERT(m_frontend);
+    ASSERT(m_client);
     m_strings->addItem(String()); // An empty string with 0 index.
 
     memset(m_edgeTypes, 0, sizeof(m_edgeTypes));
@@ -91,7 +92,7 @@ void HeapGraphSerializer::pushUpdate()
         .setEdges(m_edges.release())
         .setBaseToRealNodeId(m_baseToRealNodeIdMap.release());
 
-    m_frontend->addNativeSnapshotChunk(chunk);
+    m_client->addNativeSnapshotChunk(chunk.release());
 
     m_strings = Strings::create();
     m_edges = Edges::create();
@@ -101,6 +102,7 @@ void HeapGraphSerializer::pushUpdate()
 
 void HeapGraphSerializer::reportNode(const WTF::MemoryObjectInfo& info)
 {
+    ASSERT(info.reportedPointer());
     reportNodeImpl(info, m_nodeEdgesCount);
     m_nodeEdgesCount = 0;
     if (info.isRoot())
@@ -182,8 +184,10 @@ int HeapGraphSerializer::addString(const String& string)
 
 int HeapGraphSerializer::toNodeId(const void* to)
 {
-    ASSERT(to);
-    Address2NodeId::AddResult result = m_address2NodeIdMap.add(to, m_address2NodeIdMap.size());
+    if (!to)
+        return s_firstNodeId + m_address2NodeIdMap.size() + m_leafCount++;
+
+    Address2NodeId::AddResult result = m_address2NodeIdMap.add(to, s_firstNodeId + m_leafCount + m_address2NodeIdMap.size());
     return result.iterator->value;
 }
 
@@ -194,7 +198,7 @@ void HeapGraphSerializer::addRootNode()
 
     m_nodes->addItem(addString("Root"));
     m_nodes->addItem(0);
-    m_nodes->addItem(m_address2NodeIdMap.size());
+    m_nodes->addItem(s_firstNodeId + m_address2NodeIdMap.size() + m_leafCount);
     m_nodes->addItem(0);
     m_nodes->addItem(m_roots.size());
 }
index 6d9c9264455f68fdca1897c53ffeb1ad982241b1..0f18bfb1926db0de0e3625228c805572f2091a42 100644 (file)
 
 namespace WebCore {
 
-class HeapGraphEdge;
-class HeapGraphNode;
-class InspectorObject;
-
 class HeapGraphSerializer {
     WTF_MAKE_NONCOPYABLE(HeapGraphSerializer);
 public:
-    explicit HeapGraphSerializer(InspectorFrontend::Memory*);
+
+    class Client {
+    public:
+        virtual ~Client() { }
+        virtual void addNativeSnapshotChunk(PassRefPtr<TypeBuilder::Memory::HeapSnapshotChunk>) = 0;
+    };
+
+    explicit HeapGraphSerializer(Client*);
     ~HeapGraphSerializer();
     void reportNode(const WTF::MemoryObjectInfo&);
     void reportEdge(const void*, const char*, WTF::MemberType);
@@ -73,7 +76,7 @@ private:
     void reportEdgeImpl(const int toNodeId, const char* name, int memberType);
     int reportNodeImpl(const WTF::MemoryObjectInfo&, int edgesCount);
 
-    InspectorFrontend::Memory* m_frontend;
+    Client* m_client;
 
     typedef HashMap<String, int> StringMap;
     StringMap m_stringToIndex;
@@ -100,6 +103,9 @@ private:
 
     size_t m_edgeTypes[WTF::LastMemberTypeEntry];
     int m_unknownClassNameId;
+    int m_leafCount;
+
+    static const int s_firstNodeId = 1;
 };
 
 } // namespace WebCore
index 882ef5084bff14ec60383b0f22f341ae9f822b23..58d2bdb7d01c5337d27df1319356c6e1c18ab785 100644 (file)
@@ -333,11 +333,31 @@ void InspectorMemoryAgent::reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo)
     info.addMember(m_page, "page");
 }
 
+namespace {
+
+class FrontendWrapper : public HeapGraphSerializer::Client {
+public:
+    explicit FrontendWrapper(InspectorFrontend::Memory* frontend) : m_frontend(frontend) { }
+    virtual void addNativeSnapshotChunk(PassRefPtr<TypeBuilder::Memory::HeapSnapshotChunk> heapSnapshotChunk) OVERRIDE
+    {
+        m_frontend->addNativeSnapshotChunk(heapSnapshotChunk);
+    }
+private:
+    InspectorFrontend::Memory* m_frontend;
+};
+
+}
+
 void InspectorMemoryAgent::getProcessMemoryDistributionImpl(bool reportGraph, TypeNameToSizeMap* memoryInfo)
 {
     OwnPtr<HeapGraphSerializer> graphSerializer;
-    if (reportGraph)
-        graphSerializer = adoptPtr(new HeapGraphSerializer(m_frontend));
+    OwnPtr<FrontendWrapper> frontendWrapper;
+
+    if (reportGraph) {
+        frontendWrapper = adoptPtr(new FrontendWrapper(m_frontend));
+        graphSerializer = adoptPtr(new HeapGraphSerializer(frontendWrapper.get()));
+    }
+
     MemoryInstrumentationClientImpl memoryInstrumentationClient(graphSerializer.get());
     m_inspectorClient->getAllocatedObjects(memoryInstrumentationClient.allocatedObjects());
     MemoryInstrumentationImpl memoryInstrumentation(&memoryInstrumentationClient);
@@ -355,6 +375,8 @@ void InspectorMemoryAgent::getProcessMemoryDistributionImpl(bool reportGraph, Ty
     if (graphSerializer) {
         memoryInstrumentation.addRootObject(graphSerializer.get());
         graphSerializer->finish();
+        graphSerializer.release(); // Release it earlier than frontendWrapper
+        frontendWrapper.release();
     }
 
     m_inspectorClient->dumpUncountedAllocatedObjects(memoryInstrumentationClient.countedObjects());
index 70060ed3ff11ab3fec9201dfd4ac3a1c2a6867b4..8294e31946592ac823e901642091260d618271fe 100644 (file)
@@ -1,3 +1,48 @@
+2013-02-13  Ilya Tikhonovsky  <loislo@chromium.org>
+
+        Web Inspector: Native Memory Instrumentation: reportLeaf method doesn't report the leaf node properly.
+        https://bugs.webkit.org/show_bug.cgi?id=109554
+
+        In some cases leaves have no pointer so with the old schema we can't generate nodeId for them because we
+        can't insert 0 into hashmap. It happens when we call addPrivateBuffer method.
+
+        Drive by fix: I introduced a client interface for the HeapGraphSerializer.
+        It helps me to do the tests for the serializer.
+
+        Reviewed by Yury Semikhatsky.
+
+        It is covered by newly added tests in TestWebKitAPI.
+
+        * TestWebKitAPI/TestWebKitAPI.gypi:
+        * TestWebKitAPI/Tests/WebCore/HeapGraphSerializerTest.cpp: Added.
+        (TestWebKitAPI):
+        (HeapGraphReceiver):
+        (TestWebKitAPI::HeapGraphReceiver::HeapGraphReceiver):
+        (TestWebKitAPI::HeapGraphReceiver::printGraph):
+        (TestWebKitAPI::HeapGraphReceiver::dumpNodes):
+        (TestWebKitAPI::HeapGraphReceiver::dumpEdges):
+        (TestWebKitAPI::HeapGraphReceiver::dumpBaseToRealNodeId):
+        (TestWebKitAPI::HeapGraphReceiver::dumpStrings):
+        (TestWebKitAPI::HeapGraphReceiver::serializer):
+        (TestWebKitAPI::HeapGraphReceiver::chunkPart):
+        (TestWebKitAPI::HeapGraphReceiver::dumpPart):
+        (TestWebKitAPI::HeapGraphReceiver::stringValue):
+        (TestWebKitAPI::HeapGraphReceiver::intValue):
+        (TestWebKitAPI::HeapGraphReceiver::nodeToString):
+        (TestWebKitAPI::HeapGraphReceiver::edgeToString):
+        (TestWebKitAPI::HeapGraphReceiver::printNode):
+        (Helper):
+        (TestWebKitAPI::Helper::Helper):
+        (TestWebKitAPI::Helper::addNode):
+        (TestWebKitAPI::Helper::addEdge):
+        (TestWebKitAPI::Helper::done):
+        (Object):
+        (TestWebKitAPI::Helper::Object::Object):
+        (TestWebKitAPI::TEST):
+        (Owner):
+        (TestWebKitAPI::Owner::Owner):
+        (TestWebKitAPI::Owner::reportMemoryUsage):
+
 2013-02-18  Ryosuke Niwa  <rniwa@webkit.org>
 
         WKR build fix. Always use ascii since irclib/ircbot doesn't support unicode.
index 26ce9b7b1aa7da005e3d4dd66174f0411c28b32c..4639a34c7080a827a7d2ad409783a920c3c64704 100644 (file)
                 '<@(TestWebKitAPI_files)',
             ],
             'conditions': [
+                ['component!="shared_library"', {
+                    'sources': [
+                       '../Tests/WebCore/HeapGraphSerializerTest.cpp'
+                    ],
+                }],
                 ['inside_chromium_build==1 and component=="shared_library"', {
                     'sources': [
                         # To satisfy linking of WTF::currentTime() etc. in shared library configuration,
diff --git a/Tools/TestWebKitAPI/Tests/WebCore/HeapGraphSerializerTest.cpp b/Tools/TestWebKitAPI/Tests/WebCore/HeapGraphSerializerTest.cpp
new file mode 100644 (file)
index 0000000..6237181
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2013 Google 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:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT
+ * OWNER 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 "WTFStringUtilities.h"
+#include <gtest/gtest.h>
+
+#include "HeapGraphSerializer.h"
+#include "MemoryInstrumentationImpl.h"
+#include <wtf/MemoryInstrumentation.h>
+#include <wtf/MemoryInstrumentationHashSet.h>
+#include <wtf/MemoryInstrumentationString.h>
+#include <wtf/MemoryObjectInfo.h>
+
+namespace TestWebKitAPI {
+
+using namespace WebCore;
+
+static WTF::MemoryObjectType g_defaultObjectType = "DefaultObjectType";
+
+class HeapGraphReceiver : public HeapGraphSerializer::Client {
+public:
+    HeapGraphReceiver() : m_serializer(this) { }
+
+    virtual void addNativeSnapshotChunk(PassRefPtr<TypeBuilder::Memory::HeapSnapshotChunk> heapSnapshotChunk) OVERRIDE
+    {
+        ASSERT(!m_heapSnapshotChunk);
+        m_heapSnapshotChunk = heapSnapshotChunk;
+        m_strings = chunkPart("strings");
+        m_edges = chunkPart("edges");
+        m_nodes = chunkPart("nodes");
+
+        // Reset platform depended size field values.
+        for (InspectorArray::iterator i = m_nodes->begin(); i != m_nodes->end(); i += s_nodeFieldCount)
+            *(i + s_sizeOffset) = InspectorBasicValue::create(0);
+
+        m_id2index.clear();
+
+        for (unsigned index = 0; index < m_nodes->length(); index += s_nodeFieldCount)
+            m_id2index.add(intValue(m_nodes.get(), index + s_idOffset), index);
+    }
+
+    void printGraph()
+    {
+        EXPECT_TRUE(m_heapSnapshotChunk);
+        int processedEdgesCount = 0;
+        for (unsigned index = 0; index < m_nodes->length(); index += s_nodeFieldCount)
+            processedEdgesCount += printNode(index, processedEdgesCount);
+    }
+
+    String dumpNodes() { return dumpPart("nodes"); }
+    String dumpEdges() { return dumpPart("edges"); }
+    String dumpBaseToRealNodeId() { return dumpPart("baseToRealNodeId"); }
+    String dumpStrings() { return dumpPart("strings"); }
+
+    HeapGraphSerializer* serializer() { return &m_serializer; }
+
+private:
+    PassRefPtr<InspectorArray> chunkPart(String partName)
+    {
+        EXPECT_TRUE(m_heapSnapshotChunk);
+        RefPtr<InspectorObject> chunk = *reinterpret_cast<RefPtr<InspectorObject>*>(&m_heapSnapshotChunk);
+        RefPtr<InspectorValue> partValue = chunk->get(partName);
+        RefPtr<InspectorArray> partArray;
+        EXPECT_TRUE(partValue->asArray(&partArray));
+        return partArray.release();
+    }
+
+    String dumpPart(String partName)
+    {
+        return chunkPart(partName)->toJSONString().replace("\"", "'");
+    }
+
+    String stringValue(InspectorArray* array, int index)
+    {
+        RefPtr<InspectorValue> inspectorValue = array->get(index);
+        String value;
+        EXPECT_TRUE(inspectorValue->asString(&value));
+        return value;
+    }
+
+    int intValue(InspectorArray* array, int index)
+    {
+        RefPtr<InspectorValue> inspectorValue = array->get(index);
+        int value;
+        EXPECT_TRUE(inspectorValue->asNumber(&value));
+        return value;
+    }
+
+    String nodeToString(unsigned nodeIndex)
+    {
+        StringBuilder builder;
+        builder.append("node: ");
+        builder.appendNumber(intValue(m_nodes.get(), nodeIndex + s_idOffset));
+        builder.append(" with className:'");
+        builder.append(stringValue(m_strings.get(), intValue(m_nodes.get(), nodeIndex + s_classNameOffset)));
+        builder.append("' and name: '");
+        builder.append(stringValue(m_strings.get(), intValue(m_nodes.get(), nodeIndex + s_nameOffset)));
+        builder.append("'");
+        return builder.toString();
+    }
+
+    String edgeToString(unsigned edgeOrdinal)
+    {
+        unsigned edgeIndex = edgeOrdinal * s_edgeFieldCount;
+        StringBuilder builder;
+        builder.append("'");
+        builder.append(stringValue(m_strings.get(), intValue(m_edges.get(), edgeIndex + s_edgeTypeOffset)));
+        builder.append("' edge '");
+        builder.append(stringValue(m_strings.get(), intValue(m_edges.get(), edgeIndex + s_edgeNameOffset)));
+        builder.append("' points to ");
+        int nodeId = intValue(m_edges.get(), edgeIndex + s_toNodeIdOffset);
+        builder.append(nodeToString(m_id2index.get(nodeId)));
+        return builder.toString();
+    }
+
+    int printNode(unsigned nodeIndex, unsigned processedEdgesCount)
+    {
+        String nodeString = nodeToString(nodeIndex);
+        unsigned edgeCount = intValue(m_nodes.get(), nodeIndex + s_edgeCountOffset);
+
+        printf("%s\n", nodeString.utf8().data());
+        for (unsigned i = 0; i < edgeCount; ++i) {
+            String edgeText = edgeToString(i + processedEdgesCount);
+            printf("\thas %s\n", edgeText.utf8().data());
+        }
+        return edgeCount;
+    }
+
+    HeapGraphSerializer m_serializer;
+    RefPtr<TypeBuilder::Memory::HeapSnapshotChunk> m_heapSnapshotChunk;
+
+    RefPtr<InspectorArray> m_strings;
+    RefPtr<InspectorArray> m_nodes;
+    RefPtr<InspectorArray> m_edges;
+    HashMap<int, int> m_id2index;
+
+    static const int s_nodeFieldCount = 5;
+    static const int s_classNameOffset = 0;
+    static const int s_nameOffset = 1;
+    static const int s_idOffset = 2;
+    static const int s_sizeOffset = 3;
+    static const int s_edgeCountOffset = 4;
+
+    static const int s_edgeFieldCount = 3;
+    static const int s_edgeTypeOffset = 0;
+    static const int s_edgeNameOffset = 1;
+    static const int s_toNodeIdOffset = 2;
+};
+
+class Helper {
+public:
+    Helper(HeapGraphSerializer* serializer) : m_serializer(serializer), m_currentPointer(0) { }
+    void* addNode(const char* className, const char* name, bool isRoot)
+    {
+        WTF::MemoryObjectInfo info(0, g_defaultObjectType, ++m_currentPointer);
+        info.setClassName(className);
+        info.setName(name);
+        if (isRoot)
+            info.markAsRoot();
+        m_serializer->reportNode(info);
+        return m_currentPointer;
+    }
+
+    void addEdge(void* to, const char* edgeName, WTF::MemberType memberType)
+    {
+        m_serializer->reportEdge(to, edgeName, memberType);
+    }
+
+    void done()
+    {
+        m_serializer->finish();
+    }
+
+private:
+    HeapGraphSerializer* m_serializer;
+
+    class Object {
+    public:
+        Object() { m_data[0] = 0; }
+        char m_data[sizeof(void*)];
+    };
+    Object* m_currentPointer;
+};
+
+TEST(HeapGraphSerializerTest, snapshotWithoutUserObjects)
+{
+    HeapGraphReceiver receiver;
+    Helper helper(receiver.serializer());
+    helper.done();
+    receiver.printGraph();
+    EXPECT_EQ(String("['','weak','ownRef','countRef','unknown','Root']"), receiver.dumpStrings());
+    EXPECT_EQ(String("[5,0,1,0,0]"), receiver.dumpNodes()); // Only Root object.
+    EXPECT_EQ(String("[]"), receiver.dumpEdges()); // No edges.
+    EXPECT_EQ(String("[]"), receiver.dumpBaseToRealNodeId()); // No id maps.
+}
+
+TEST(HeapGraphSerializerTest, oneRootUserObject)
+{
+    HeapGraphReceiver receiver;
+    Helper helper(receiver.serializer());
+    helper.addNode("ClassName", "objectName", true);
+    helper.done();
+    receiver.printGraph();
+    EXPECT_EQ(String("['','weak','ownRef','countRef','unknown','ClassName','objectName','Root']"), receiver.dumpStrings());
+    EXPECT_EQ(String("[5,6,1,0,0,7,0,2,0,1]"), receiver.dumpNodes());
+    EXPECT_EQ(String("[1,0,1]"), receiver.dumpEdges());
+    EXPECT_EQ(String("[]"), receiver.dumpBaseToRealNodeId());
+}
+
+TEST(HeapGraphSerializerTest, twoUserObjectsWithEdge)
+{
+    HeapGraphReceiver receiver;
+    Helper helper(receiver.serializer());
+    void* childObject = helper.addNode("Child", "child", false);
+    helper.addEdge(childObject, "pointerToChild", WTF::OwnPtrMember);
+    helper.addNode("Parent", "parent", true);
+    helper.done();
+    receiver.printGraph();
+    EXPECT_EQ(String("['','weak','ownRef','countRef','unknown','Child','child','pointerToChild','Parent','parent','Root']"), receiver.dumpStrings());
+    EXPECT_EQ(String("[5,6,1,0,0,8,9,2,0,1,10,0,3,0,1]"), receiver.dumpNodes());
+    EXPECT_EQ(String("[2,7,1,1,0,2]"), receiver.dumpEdges());
+    EXPECT_EQ(String("[]"), receiver.dumpBaseToRealNodeId());
+}
+
+class Owner {
+public:
+    Owner()
+    {
+        m_strings.add("first element");
+        m_strings.add("second element");
+    }
+    void reportMemoryUsage(WTF::MemoryObjectInfo* memoryObjectInfo) const
+    {
+        WTF::MemoryClassInfo info(memoryObjectInfo, this, g_defaultObjectType);
+        info.addMember(m_strings, "strings");
+    }
+private:
+    HashSet<String> m_strings;
+};
+
+TEST(HeapGraphSerializerTest, hashSetWithTwoStrings)
+{
+    HeapGraphReceiver receiver;
+    MemoryInstrumentationClientImpl memoryInstrumentationClient(receiver.serializer());
+    MemoryInstrumentationImpl memoryInstrumentation(&memoryInstrumentationClient);
+
+    Owner owner;
+    memoryInstrumentation.addRootObject(&owner);
+    receiver.serializer()->finish();
+    receiver.printGraph();
+    EXPECT_EQ(String("[5,0,1,0,0,8,0,4,0,3,9,0,3,0,0,9,0,2,0,0,10,0,5,0,1]"), receiver.dumpNodes());
+    EXPECT_EQ(String("[2,6,1,1,7,2,1,7,3,1,0,4]"), receiver.dumpEdges());
+    EXPECT_EQ(String("[]"), receiver.dumpBaseToRealNodeId());
+}
+
+} // namespace