1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "WTFStringUtilities.h"
33#include <gtest/gtest.h>
34
35#include "HeapGraphSerializer.h"
36#include "MemoryInstrumentationImpl.h"
37#include <wtf/MemoryInstrumentation.h>
38#include <wtf/MemoryInstrumentationHashSet.h>
39#include <wtf/MemoryInstrumentationString.h>
40#include <wtf/MemoryObjectInfo.h>
41
42namespace {
43
44using namespace WebCore;
45
46static WTF::MemoryObjectType g_defaultObjectType = "DefaultObjectType";
47
48class HeapGraphReceiver : public HeapGraphSerializerClient {
49public:
50 HeapGraphReceiver() : m_serializer(this)
51 {
52 m_currentPointer = 0;
53 }
54
55 virtual void addNativeSnapshotChunk(PassRefPtr<TypeBuilder::Memory::HeapSnapshotChunk> heapSnapshotChunk) OVERRIDE
56 {
57 m_heapSnapshotChunk = heapSnapshotChunk;
58 m_strings = chunkPart("strings");
59 m_edges = chunkPart("edges");
60 m_nodes = chunkPart("nodes");
61
62 // Reset platform depended size field values.
63 for (InspectorArray::iterator i = m_nodes->begin(); i != m_nodes->end(); i += s_nodeFieldCount)
64 *(i + s_sizeOffset) = InspectorBasicValue::create(0);
65
66 m_id2index.clear();
67
68 for (int index = 0; index < m_nodes->length(); index += s_nodeFieldCount)
69 m_id2index.add(intValue(m_nodes.get(), index + s_idOffset), index);
70 }
71
72 void printGraph()
73 {
74 EXPECT_TRUE(m_heapSnapshotChunk);
75 int processedEdgesCount = 0;
76 for (unsigned index = 0; index < m_nodes->length(); index += s_nodeFieldCount)
77 processedEdgesCount += printNode(index, processedEdgesCount);
78 }
79
80 String dumpLastChunkNodes() { return dumpLastChunkPart("nodes"); }
81 String dumpLastChunkEdges() { return dumpLastChunkPart("edges"); }
82 String dumpLastChunkBaseToRealNodeId() { return dumpLastChunkPart("baseToRealNodeId"); }
83 String dumpLastChunkStrings() { return dumpLastChunkPart("strings"); }
84
85 void* addNode(const char* className, const char* name, bool isRoot)
86 {
87 WTF::MemoryObjectInfo info(0, g_defaultObjectType, ++m_currentPointer);
88 info.setClassName(className);
89 info.setName(name);
90 if (isRoot)
91 info.markAsRoot();
92 m_serializer.reportNode(info);
93 return m_currentPointer;
94 }
95
96 void addEdge(void* to, const char* edgeName, WTF::MemberType memberType)
97 {
98 m_serializer.reportEdge(to, edgeName, memberType);
99 }
100
101 void done()
102 {
103 m_serializer.finish();
104 printGraph();
105 }
106
107 HeapGraphSerializer* heapGraphSerializer() { return &m_serializer; }
108
109private:
110 PassRefPtr<InspectorArray> chunkPart(String partName)
111 {
112 EXPECT_TRUE(m_heapSnapshotChunk);
113 RefPtr<InspectorObject> chunk = *reinterpret_cast<RefPtr<InspectorObject>*>(&m_heapSnapshotChunk);
114 RefPtr<InspectorValue> partValue = chunk->get(partName);
115 RefPtr<InspectorArray> partArray;
116 EXPECT_TRUE(partValue->asArray(&partArray));
117 return partArray.release();
118 }
119
120 String dumpLastChunkPart(String partName)
121 {
122 return chunkPart(partName)->toJSONString().replace("\"", "'");
123 }
124
125 String stringValue(InspectorArray* array, int index)
126 {
127 RefPtr<InspectorValue> inspectorValue = array->get(index);
128 String value;
129 EXPECT_TRUE(inspectorValue->asString(&value));
130 return value;
131 }
132
133 int intValue(InspectorArray* array, int index)
134 {
135 RefPtr<InspectorValue> inspectorValue = array->get(index);
136 int value;
137 EXPECT_TRUE(inspectorValue->asNumber(&value));
138 return value;
139 }
140
141 String node(unsigned nodeIndex)
142 {
143 StringBuilder builder;
144 builder.append("node: ");
145 builder.appendNumber(intValue(m_nodes.get(), nodeIndex + s_idOffset));
146 builder.append(" with className:'");
147 builder.append(stringValue(m_strings.get(), intValue(m_nodes.get(), nodeIndex + s_classNameOffset)));
148 builder.append("' and name: '");
149 builder.append(stringValue(m_strings.get(), intValue(m_nodes.get(), nodeIndex + s_nameOffset)));
150 builder.append("'");
151 return builder.toString();
152 }
153
154 String edge(unsigned edgeOrdinal)
155 {
156 unsigned edgeIndex = edgeOrdinal * s_edgeFieldCount;
157 StringBuilder builder;
158 builder.append("'");
159 builder.append(stringValue(m_strings.get(), intValue(m_edges.get(), edgeIndex + s_edgeTypeOffset)));
160 builder.append("' edge '");
161 builder.append(stringValue(m_strings.get(), intValue(m_edges.get(), edgeIndex + s_edgeNameOffset)));
162 builder.append("' points to ");
163 int nodeId = intValue(m_edges.get(), edgeIndex + s_toNodeIdOffset);
164 builder.append(node(m_id2index.get(nodeId)));
165 return builder.toString();
166 }
167
168 int printNode(unsigned nodeIndex, unsigned processedEdgesCount)
169 {
170 String nodeString = node(nodeIndex);
171 int edgeCount = intValue(m_nodes.get(), nodeIndex + s_edgeCountOffset);
172
173 printf("%s\n", nodeString.utf8().data());
174 for (unsigned i = 0; i < edgeCount; ++i) {
175 String edgeText = edge(i + processedEdgesCount);
176 printf("\thas %s\n", edgeText.utf8().data());
177 }
178 return edgeCount;
179 }
180
181 HeapGraphSerializer m_serializer;
182 RefPtr<TypeBuilder::Memory::HeapSnapshotChunk> m_heapSnapshotChunk;
183
184 RefPtr<InspectorArray> m_strings;
185 RefPtr<InspectorArray> m_nodes;
186 RefPtr<InspectorArray> m_edges;
187 HashMap<int, int> m_id2index;
188
189 class Object {
190 char m_data[sizeof(void*)];
191 };
192 Object* m_currentPointer;
193
194 static const int s_nodeFieldCount = 5;
195 static const int s_classNameOffset = 0;
196 static const int s_nameOffset = 1;
197 static const int s_idOffset = 2;
198 static const int s_sizeOffset = 3;
199 static const int s_edgeCountOffset = 4;
200
201 static const int s_edgeFieldCount = 3;
202 static const int s_edgeTypeOffset = 0;
203 static const int s_edgeNameOffset = 1;
204 static const int s_toNodeIdOffset = 2;
205};
206
207TEST(HeapGraphSerializerTest, snapshotWithoutUserObjects)
208{
209 HeapGraphReceiver receiver;
210 receiver.done();
211 EXPECT_EQ(String("['','weak','ownRef','countRef','unknown','Root']"), receiver.dumpLastChunkStrings());
212 EXPECT_EQ(String("[5,0,1,0,0]"), receiver.dumpLastChunkNodes()); // Only Root object.
213 EXPECT_EQ(String("[]"), receiver.dumpLastChunkEdges()); // No edges.
214 EXPECT_EQ(String("[]"), receiver.dumpLastChunkBaseToRealNodeId()); // No id maps.
215}
216
217TEST(HeapGraphSerializerTest, oneRootUserObject)
218{
219 HeapGraphReceiver receiver;
220 receiver.addNode("ClassName", "objectName", true);
221 receiver.done();
222 EXPECT_EQ(String("['','weak','ownRef','countRef','unknown','ClassName','objectName','Root']"), receiver.dumpLastChunkStrings());
223 EXPECT_EQ(String("[5,6,1,0,0,7,0,2,0,1]"), receiver.dumpLastChunkNodes());
224 EXPECT_EQ(String("[1,0,1]"), receiver.dumpLastChunkEdges());
225 EXPECT_EQ(String("[]"), receiver.dumpLastChunkBaseToRealNodeId());
226}
227
228TEST(HeapGraphSerializerTest, twoUserObjectsWithEdge)
229{
230 HeapGraphReceiver receiver;
231 void* childObject = receiver.addNode("Child", "child", false);
232 receiver.addEdge(childObject, "pointerToChild", WTF::OwnPtrMember);
233 receiver.addNode("Parent", "parent", true);
234 receiver.done();
235 EXPECT_EQ(String("['','weak','ownRef','countRef','unknown','Child','child','pointerToChild','Parent','parent','Root']"), receiver.dumpLastChunkStrings());
236 EXPECT_EQ(String("[5,6,1,0,0,8,9,2,0,1,10,0,3,0,1]"), receiver.dumpLastChunkNodes());
237 EXPECT_EQ(String("[2,7,1,1,0,2]"), receiver.dumpLastChunkEdges());
238 EXPECT_EQ(String("[]"), receiver.dumpLastChunkBaseToRealNodeId());
239}
240
241class Owner {
242public:
243 Owner()
244 {
245 m_strings.add("first element");
246 m_strings.add("second element");
247 }
248 void reportMemoryUsage(WTF::MemoryObjectInfo* memoryObjectInfo) const
249 {
250 WTF::MemoryClassInfo info(memoryObjectInfo, this, g_defaultObjectType);
251 info.addMember(m_strings, "strings");
252 }
253private:
254 HashSet<String> m_strings;
255};
256
257TEST(HeapGraphSerializerTest, hashSetWithTwoItems)
258{
259 HeapGraphReceiver receiver;
260 MemoryInstrumentationClientImpl memoryInstrumentationClient(receiver.heapGraphSerializer());
261 MemoryInstrumentationImpl memoryInstrumentation(&memoryInstrumentationClient);
262
263 Owner owner;
264 memoryInstrumentation.addRootObject(&owner);
265 receiver.done();
266 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.dumpLastChunkNodes());
267 EXPECT_EQ(String("[2,6,1,1,7,2,1,7,3,1,0,4]"), receiver.dumpLastChunkEdges());
268 EXPECT_EQ(String("[]"), receiver.dumpLastChunkBaseToRealNodeId());
269}
270
271} // namespace