OLD | NEW |
| (Empty) |
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 | |
33 #include "HeapGraphSerializer.h" | |
34 #include "MemoryInstrumentationImpl.h" | |
35 #include "wtf/Assertions.h" | |
36 #include "wtf/MemoryInstrumentation.h" | |
37 #include "wtf/MemoryInstrumentationHashSet.h" | |
38 #include "wtf/MemoryInstrumentationString.h" | |
39 #include "wtf/MemoryObjectInfo.h" | |
40 #include "wtf/text/CString.h" | |
41 #include "wtf/text/StringBuilder.h" | |
42 #include "wtf/text/WTFString.h" | |
43 #include <gtest/gtest.h> | |
44 | |
45 namespace WTF { | |
46 | |
47 static std::ostream& operator<<(std::ostream& os, const String& string) | |
48 { | |
49 return os << string.utf8().data(); | |
50 } | |
51 | |
52 } | |
53 | |
54 namespace { | |
55 | |
56 using namespace WebCore; | |
57 | |
58 static WTF::MemoryObjectType g_defaultObjectType = "DefaultObjectType"; | |
59 | |
60 class HeapGraphReceiver : public HeapGraphSerializer::Client { | |
61 public: | |
62 HeapGraphReceiver() : m_serializer(this) { } | |
63 | |
64 virtual void addNativeSnapshotChunk(PassRefPtr<TypeBuilder::Memory::HeapSnap
shotChunk> heapSnapshotChunk) OVERRIDE | |
65 { | |
66 ASSERT(!m_heapSnapshotChunk); | |
67 m_heapSnapshotChunk = heapSnapshotChunk; | |
68 m_strings = chunkPart("strings"); | |
69 m_edges = chunkPart("edges"); | |
70 m_nodes = chunkPart("nodes"); | |
71 | |
72 // Reset platform depended size field values. | |
73 for (InspectorArray::iterator i = m_nodes->begin(); i != m_nodes->end();
i += s_nodeFieldCount) | |
74 *(i + s_sizeOffset) = InspectorBasicValue::create(0); | |
75 | |
76 m_id2index.clear(); | |
77 | |
78 for (unsigned index = 0; index < m_nodes->length(); index += s_nodeField
Count) | |
79 m_id2index.add(intValue(m_nodes.get(), index + s_idOffset), index); | |
80 } | |
81 | |
82 void printGraph() | |
83 { | |
84 EXPECT_TRUE(m_heapSnapshotChunk); | |
85 int processedEdgesCount = 0; | |
86 for (unsigned index = 0; index < m_nodes->length(); index += s_nodeField
Count) | |
87 processedEdgesCount += printNode(index, processedEdgesCount); | |
88 } | |
89 | |
90 String dumpNodes() { return dumpPart("nodes"); } | |
91 String dumpEdges() { return dumpPart("edges"); } | |
92 String dumpBaseToRealNodeId() { return dumpPart("baseToRealNodeId"); } | |
93 String dumpStrings() { return dumpPart("strings"); } | |
94 | |
95 HeapGraphSerializer* serializer() { return &m_serializer; } | |
96 | |
97 private: | |
98 PassRefPtr<InspectorArray> chunkPart(String partName) | |
99 { | |
100 EXPECT_TRUE(m_heapSnapshotChunk); | |
101 RefPtr<InspectorObject> chunk = *reinterpret_cast<RefPtr<InspectorObject
>*>(&m_heapSnapshotChunk); | |
102 RefPtr<InspectorValue> partValue = chunk->get(partName); | |
103 RefPtr<InspectorArray> partArray; | |
104 EXPECT_TRUE(partValue->asArray(&partArray)); | |
105 return partArray.release(); | |
106 } | |
107 | |
108 String dumpPart(String partName) | |
109 { | |
110 return chunkPart(partName)->toJSONString().replace("\"", "'"); | |
111 } | |
112 | |
113 String stringValue(InspectorArray* array, int index) | |
114 { | |
115 RefPtr<InspectorValue> inspectorValue = array->get(index); | |
116 String value; | |
117 EXPECT_TRUE(inspectorValue->asString(&value)); | |
118 return value; | |
119 } | |
120 | |
121 int intValue(InspectorArray* array, int index) | |
122 { | |
123 RefPtr<InspectorValue> inspectorValue = array->get(index); | |
124 int value; | |
125 EXPECT_TRUE(inspectorValue->asNumber(&value)); | |
126 return value; | |
127 } | |
128 | |
129 String nodeToString(unsigned nodeIndex) | |
130 { | |
131 StringBuilder builder; | |
132 builder.append("node: "); | |
133 builder.appendNumber(intValue(m_nodes.get(), nodeIndex + s_idOffset)); | |
134 builder.append(" with className:'"); | |
135 builder.append(stringValue(m_strings.get(), intValue(m_nodes.get(), node
Index + s_classNameOffset))); | |
136 builder.append("' and name: '"); | |
137 builder.append(stringValue(m_strings.get(), intValue(m_nodes.get(), node
Index + s_nameOffset))); | |
138 builder.append("'"); | |
139 return builder.toString(); | |
140 } | |
141 | |
142 String edgeToString(unsigned edgeOrdinal) | |
143 { | |
144 unsigned edgeIndex = edgeOrdinal * s_edgeFieldCount; | |
145 StringBuilder builder; | |
146 builder.append("'"); | |
147 builder.append(stringValue(m_strings.get(), intValue(m_edges.get(), edge
Index + s_edgeTypeOffset))); | |
148 builder.append("' edge '"); | |
149 builder.append(stringValue(m_strings.get(), intValue(m_edges.get(), edge
Index + s_edgeNameOffset))); | |
150 builder.append("' points to "); | |
151 int nodeId = intValue(m_edges.get(), edgeIndex + s_toNodeIdOffset); | |
152 builder.append(nodeToString(m_id2index.get(nodeId))); | |
153 return builder.toString(); | |
154 } | |
155 | |
156 int printNode(unsigned nodeIndex, unsigned processedEdgesCount) | |
157 { | |
158 String nodeString = nodeToString(nodeIndex); | |
159 unsigned edgeCount = intValue(m_nodes.get(), nodeIndex + s_edgeCountOffs
et); | |
160 | |
161 printf("%s\n", nodeString.utf8().data()); | |
162 for (unsigned i = 0; i < edgeCount; ++i) { | |
163 String edgeText = edgeToString(i + processedEdgesCount); | |
164 printf("\thas %s\n", edgeText.utf8().data()); | |
165 } | |
166 return edgeCount; | |
167 } | |
168 | |
169 HeapGraphSerializer m_serializer; | |
170 RefPtr<TypeBuilder::Memory::HeapSnapshotChunk> m_heapSnapshotChunk; | |
171 | |
172 RefPtr<InspectorArray> m_strings; | |
173 RefPtr<InspectorArray> m_nodes; | |
174 RefPtr<InspectorArray> m_edges; | |
175 HashMap<int, int> m_id2index; | |
176 | |
177 static const int s_nodeFieldCount = 5; | |
178 static const int s_classNameOffset = 0; | |
179 static const int s_nameOffset = 1; | |
180 static const int s_idOffset = 2; | |
181 static const int s_sizeOffset = 3; | |
182 static const int s_edgeCountOffset = 4; | |
183 | |
184 static const int s_edgeFieldCount = 3; | |
185 static const int s_edgeTypeOffset = 0; | |
186 static const int s_edgeNameOffset = 1; | |
187 static const int s_toNodeIdOffset = 2; | |
188 }; | |
189 | |
190 class Helper { | |
191 public: | |
192 Helper(HeapGraphSerializer* serializer) | |
193 : m_serializer(serializer) | |
194 , m_memoryInstrumentationClient(serializer) | |
195 , m_memoryInstrumentation(&m_memoryInstrumentationClient) | |
196 , m_currentPointer(0) | |
197 { } | |
198 | |
199 void* addNode(const char* className, const char* name, bool isRoot) | |
200 { | |
201 WTF::MemoryObjectInfo info(&m_memoryInstrumentation, g_defaultObjectType
, ++m_currentPointer); | |
202 info.setClassName(className); | |
203 info.setName(name); | |
204 if (isRoot) | |
205 info.markAsRoot(); | |
206 m_serializer->reportNode(info); | |
207 return m_currentPointer; | |
208 } | |
209 | |
210 void addEdge(void* to, const char* edgeName, WTF::MemberType memberType) | |
211 { | |
212 m_serializer->reportEdge(to, edgeName, memberType); | |
213 } | |
214 | |
215 void done() | |
216 { | |
217 m_serializer->finish(); | |
218 } | |
219 | |
220 private: | |
221 HeapGraphSerializer* m_serializer; | |
222 MemoryInstrumentationClientImpl m_memoryInstrumentationClient; | |
223 MemoryInstrumentationImpl m_memoryInstrumentation; | |
224 | |
225 class Object { | |
226 public: | |
227 Object() { m_data[0] = 0; } | |
228 char m_data[sizeof(void*)]; | |
229 }; | |
230 Object* m_currentPointer; | |
231 }; | |
232 | |
233 TEST(HeapGraphSerializerTest, snapshotWithoutUserObjects) | |
234 { | |
235 HeapGraphReceiver receiver; | |
236 Helper helper(receiver.serializer()); | |
237 helper.done(); | |
238 receiver.printGraph(); | |
239 EXPECT_EQ(String("['','weak','property','object','unknown','Root']"), receiv
er.dumpStrings()); | |
240 EXPECT_EQ(String("[5,0,1,0,0]"), receiver.dumpNodes()); // Only Root object. | |
241 EXPECT_EQ(String("[]"), receiver.dumpEdges()); // No edges. | |
242 EXPECT_EQ(String("[]"), receiver.dumpBaseToRealNodeId()); // No id maps. | |
243 } | |
244 | |
245 TEST(HeapGraphSerializerTest, oneRootUserObject) | |
246 { | |
247 HeapGraphReceiver receiver; | |
248 Helper helper(receiver.serializer()); | |
249 helper.addNode("ClassName", "objectName", true); | |
250 helper.done(); | |
251 receiver.printGraph(); | |
252 EXPECT_EQ(String("['','weak','property','object','unknown','ClassName','obje
ctName','Root']"), receiver.dumpStrings()); | |
253 EXPECT_EQ(String("[5,6,1,0,0,7,0,2,0,1]"), receiver.dumpNodes()); | |
254 EXPECT_EQ(String("[1,0,1]"), receiver.dumpEdges()); | |
255 EXPECT_EQ(String("[]"), receiver.dumpBaseToRealNodeId()); | |
256 } | |
257 | |
258 TEST(HeapGraphSerializerTest, twoUserObjectsWithEdge) | |
259 { | |
260 HeapGraphReceiver receiver; | |
261 Helper helper(receiver.serializer()); | |
262 void* childObject = helper.addNode("Child", "child", false); | |
263 helper.addEdge(childObject, "pointerToChild", WTF::RetainingPointer); | |
264 helper.addNode("Parent", "parent", true); | |
265 helper.done(); | |
266 receiver.printGraph(); | |
267 EXPECT_EQ(String("['','weak','property','object','unknown','Child','child','
pointerToChild','Parent','parent','Root']"), receiver.dumpStrings()); | |
268 EXPECT_EQ(String("[5,6,1,0,0,8,9,2,0,1,10,0,3,0,1]"), receiver.dumpNodes()); | |
269 EXPECT_EQ(String("[2,7,1,1,0,2]"), receiver.dumpEdges()); | |
270 EXPECT_EQ(String("[]"), receiver.dumpBaseToRealNodeId()); | |
271 } | |
272 | |
273 class Owner { | |
274 public: | |
275 Owner() | |
276 { | |
277 m_strings.add("first element"); | |
278 m_strings.add("second element"); | |
279 } | |
280 void reportMemoryUsage(WTF::MemoryObjectInfo* memoryObjectInfo) const | |
281 { | |
282 WTF::MemoryClassInfo info(memoryObjectInfo, this, g_defaultObjectType); | |
283 info.addMember(m_strings, "strings"); | |
284 } | |
285 private: | |
286 HashSet<String> m_strings; | |
287 }; | |
288 | |
289 TEST(HeapGraphSerializerTest, hashSetWithTwoStrings) | |
290 { | |
291 HeapGraphReceiver receiver; | |
292 MemoryInstrumentationClientImpl memoryInstrumentationClient(receiver.seriali
zer()); | |
293 MemoryInstrumentationImpl memoryInstrumentation(&memoryInstrumentationClient
); | |
294 | |
295 Owner owner; | |
296 memoryInstrumentation.addRootObject(&owner); | |
297 receiver.serializer()->finish(); | |
298 receiver.printGraph(); | |
299 EXPECT_EQ(String("[6,0,1,0,0,5,0,4,0,3,9,0,3,0,0,9,0,2,0,0,10,0,5,0,1]"), re
ceiver.dumpNodes()); | |
300 EXPECT_EQ(String("[2,7,1,2,8,2,2,8,3,1,0,4]"), receiver.dumpEdges()); | |
301 EXPECT_EQ(String("[]"), receiver.dumpBaseToRealNodeId()); | |
302 } | |
303 | |
304 } // namespace | |
OLD | NEW |