| /* |
| * Copyright (C) 2012 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 "wtf/ArrayBuffer.h" |
| #include "wtf/HashCountedSet.h" |
| #include "wtf/HashMap.h" |
| #include "wtf/HashSet.h" |
| #include "wtf/ListHashSet.h" |
| #include "wtf/MemoryInstrumentation.h" |
| #include "wtf/MemoryInstrumentationArrayBufferView.h" |
| #include "wtf/MemoryInstrumentationHashCountedSet.h" |
| #include "wtf/MemoryInstrumentationHashMap.h" |
| #include "wtf/MemoryInstrumentationHashSet.h" |
| #include "wtf/MemoryInstrumentationListHashSet.h" |
| #include "wtf/MemoryInstrumentationString.h" |
| #include "wtf/MemoryInstrumentationVector.h" |
| #include "wtf/MemoryObjectInfo.h" |
| #include "wtf/RefCounted.h" |
| #include "wtf/Vector.h" |
| #include "wtf/text/AtomicString.h" |
| #include "wtf/text/CString.h" |
| #include "wtf/text/StringBuffer.h" |
| #include "wtf/text/StringHash.h" |
| #include "wtf/text/StringImpl.h" |
| #include "wtf/text/WTFString.h" |
| #include <gtest/gtest.h> |
| |
| namespace { |
| enum TestEnum { ONE = 1, TWO, THREE, MY_ENUM_MAX }; |
| } |
| |
| namespace WTF { |
| |
| template<> struct DefaultHash<TestEnum> { |
| typedef IntHash<unsigned> Hash; |
| }; |
| |
| template<> struct HashTraits<TestEnum> : GenericHashTraits<TestEnum> { |
| static const bool emptyValueIsZero = true; |
| static const bool needsDestruction = false; |
| static void constructDeletedValue(TestEnum& slot) { slot = static_cast<TestEnum>(MY_ENUM_MAX + 1); } |
| static bool isDeletedValue(TestEnum value) { return value == (MY_ENUM_MAX + 1); } |
| }; |
| |
| } |
| |
| namespace { |
| |
| using WTF::MemoryObjectInfo; |
| using WTF::MemoryClassInfo; |
| using WTF::MemoryObjectType; |
| using WTF::MemberType; |
| |
| MemoryObjectType TestType = "TestType"; |
| |
| class MemoryInstrumentationTestClient : public WTF::MemoryInstrumentationClient { |
| public: |
| MemoryInstrumentationTestClient() : m_links(WTF::LastMemberTypeEntry) |
| { |
| m_links[WTF::PointerMember] = 0; |
| m_links[WTF::ReferenceMember] = 0; |
| m_links[WTF::RetainingPointer] = 0; |
| } |
| |
| virtual void countObjectSize(const void*, MemoryObjectType objectType, size_t size) |
| { |
| TypeToSizeMap::AddResult result = m_totalSizes.add(objectType, size); |
| if (!result.isNewEntry) |
| result.iterator->value += size; |
| } |
| virtual bool visited(const void* object) { return !m_visitedObjects.add(object).isNewEntry; } |
| virtual bool checkCountedObject(const void*) { return true; } |
| virtual void reportNode(const MemoryObjectInfo&) OVERRIDE { } |
| virtual void reportEdge(const void*, const char*, MemberType memberType) OVERRIDE |
| { |
| ++m_links[memberType]; |
| } |
| virtual void reportLeaf(const MemoryObjectInfo&, const char*) OVERRIDE |
| { |
| ++m_links[WTF::RetainingPointer]; |
| } |
| virtual void reportBaseAddress(const void*, const void*) OVERRIDE { } |
| virtual int registerString(const char*) OVERRIDE { return -1; } |
| |
| size_t visitedObjects() const { return m_visitedObjects.size(); } |
| size_t totalSize(const MemoryObjectType objectType) const |
| { |
| TypeToSizeMap::const_iterator i = m_totalSizes.find(objectType); |
| return i == m_totalSizes.end() ? 0 : i->value; |
| } |
| |
| size_t linksCount(const WTF::MemberType memberType) const |
| { |
| return m_links[memberType]; |
| } |
| |
| size_t reportedSizeForAllTypes() const |
| { |
| size_t size = 0; |
| for (TypeToSizeMap::const_iterator i = m_totalSizes.begin(); i != m_totalSizes.end(); ++i) |
| size += i->value; |
| return size; |
| } |
| |
| private: |
| typedef HashMap<MemoryObjectType, size_t> TypeToSizeMap; |
| TypeToSizeMap m_totalSizes; |
| WTF::HashSet<const void*> m_visitedObjects; |
| WTF::Vector<size_t, WTF::LastMemberTypeEntry> m_links; |
| }; |
| |
| class InstrumentationTestImpl : public WTF::MemoryInstrumentation { |
| public: |
| explicit InstrumentationTestImpl(MemoryInstrumentationTestClient* client) |
| : MemoryInstrumentation(client) |
| , m_client(client) { } |
| |
| virtual void processDeferredObjects(); |
| virtual void deferObject(PassOwnPtr<WrapperBase>); |
| |
| size_t visitedObjects() const { return m_client->visitedObjects(); } |
| size_t reportedSizeForAllTypes() const { return m_client->reportedSizeForAllTypes(); } |
| size_t totalSize(const MemoryObjectType objectType) const { return m_client->totalSize(objectType); } |
| size_t linksCount(const WTF::MemberType memberType) const { return m_client->linksCount(memberType); } |
| |
| private: |
| MemoryInstrumentationTestClient* m_client; |
| Vector<OwnPtr<WrapperBase> > m_deferredObjects; |
| }; |
| |
| class InstrumentationTestHelper : public InstrumentationTestImpl { |
| public: |
| InstrumentationTestHelper() : InstrumentationTestImpl(&m_client) { } |
| |
| private: |
| MemoryInstrumentationTestClient m_client; |
| }; |
| |
| void InstrumentationTestImpl::processDeferredObjects() |
| { |
| while (!m_deferredObjects.isEmpty()) { |
| OwnPtr<WrapperBase> pointer = m_deferredObjects.last().release(); |
| m_deferredObjects.removeLast(); |
| pointer->process(this); |
| } |
| } |
| |
| void InstrumentationTestImpl::deferObject(PassOwnPtr<WrapperBase> pointer) |
| { |
| m_deferredObjects.append(pointer); |
| } |
| |
| class NotInstrumented { |
| public: |
| NotInstrumented(const char* = 0) { } |
| char m_data[42]; |
| }; |
| |
| class Instrumented { |
| public: |
| Instrumented() : m_notInstrumented(new NotInstrumented) { } |
| virtual ~Instrumented() { disposeOwnedObject(); } |
| |
| virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| memoryObjectInfo->setClassName("Instrumented"); |
| info.addMember(m_notInstrumented, "m_notInstrumented", WTF::RetainingPointer); |
| } |
| |
| void disposeOwnedObject() |
| { |
| delete m_notInstrumented; |
| m_notInstrumented = 0; |
| } |
| |
| NotInstrumented* m_notInstrumented; |
| }; |
| |
| TEST(MemoryInstrumentationTest, sizeOf) |
| { |
| InstrumentationTestHelper helper; |
| Instrumented instrumented; |
| helper.addRootObject(instrumented); |
| EXPECT_EQ(sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| EXPECT_EQ(1u, helper.linksCount(WTF::RetainingPointer)); |
| } |
| |
| TEST(MemoryInstrumentationTest, nullCheck) |
| { |
| InstrumentationTestHelper helper; |
| Instrumented* instrumented = 0; |
| helper.addRootObject(instrumented); |
| EXPECT_EQ(0u, helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(0u, helper.visitedObjects()); |
| EXPECT_EQ(0u, helper.linksCount(WTF::RetainingPointer)); |
| } |
| |
| TEST(MemoryInstrumentationTest, ptrVsRef) |
| { |
| { |
| InstrumentationTestHelper helper; |
| Instrumented instrumented; |
| helper.addRootObject(&instrumented); |
| EXPECT_EQ(sizeof(Instrumented) + sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| EXPECT_EQ(1u, helper.linksCount(WTF::RetainingPointer)); |
| } |
| { |
| InstrumentationTestHelper helper; |
| Instrumented instrumented; |
| helper.addRootObject(instrumented); |
| EXPECT_EQ(sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| EXPECT_EQ(1u, helper.linksCount(WTF::RetainingPointer)); |
| } |
| } |
| |
| class InstrumentedWithOwnPtr : public Instrumented { |
| public: |
| InstrumentedWithOwnPtr() : m_notInstrumentedOwnPtr(adoptPtr(new NotInstrumented)) { } |
| |
| virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| Instrumented::reportMemoryUsage(memoryObjectInfo); |
| info.addMember(m_notInstrumentedOwnPtr); |
| } |
| OwnPtr<NotInstrumented> m_notInstrumentedOwnPtr; |
| }; |
| |
| TEST(MemoryInstrumentationTest, ownPtrNotInstrumented) |
| { |
| InstrumentationTestHelper helper; |
| InstrumentedWithOwnPtr instrumentedWithOwnPtr; |
| helper.addRootObject(instrumentedWithOwnPtr); |
| EXPECT_EQ(2u * sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| EXPECT_EQ(2u, helper.linksCount(WTF::RetainingPointer)); |
| } |
| |
| class InstrumentedUndefined { |
| public: |
| InstrumentedUndefined() : m_data(0) { } |
| |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this); |
| } |
| int m_data; |
| }; |
| |
| class InstrumentedDOM { |
| public: |
| InstrumentedDOM() : m_instrumentedUndefined(adoptPtr(new InstrumentedUndefined)) { } |
| |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_instrumentedUndefined); |
| } |
| OwnPtr<InstrumentedUndefined> m_instrumentedUndefined; |
| }; |
| |
| TEST(MemoryInstrumentationTest, ownerTypePropagation) |
| { |
| InstrumentationTestHelper helper; |
| OwnPtr<InstrumentedDOM> instrumentedDOM(adoptPtr(new InstrumentedDOM)); |
| helper.addRootObject(instrumentedDOM.get()); |
| EXPECT_EQ(sizeof(InstrumentedDOM) + sizeof(InstrumentedUndefined), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(sizeof(InstrumentedDOM) + sizeof(InstrumentedUndefined), helper.totalSize(TestType)); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| } |
| |
| class NonVirtualInstrumented { |
| public: |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_instrumented); |
| } |
| |
| Instrumented m_instrumented; |
| }; |
| |
| TEST(MemoryInstrumentationTest, visitFirstMemberInNonVirtualClass) |
| { |
| InstrumentationTestHelper helper; |
| NonVirtualInstrumented nonVirtualInstrumented; |
| helper.addRootObject(&nonVirtualInstrumented); |
| EXPECT_EQ(sizeof(NonVirtualInstrumented) + sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| } |
| |
| template<typename T> |
| class InstrumentedOwner { |
| public: |
| template<typename V> |
| InstrumentedOwner(const V& value) : m_value(value) { } |
| InstrumentedOwner() { } |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_value, "value", WTF::RetainingPointer); |
| } |
| |
| T m_value; |
| }; |
| |
| TEST(MemoryInstrumentationTest, visitStrings) |
| { |
| { // 8-bit string. |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<String> stringInstrumentedOwner("String"); |
| helper.addRootObject(stringInstrumentedOwner); |
| EXPECT_EQ(sizeof(StringImpl) + stringInstrumentedOwner.m_value.length(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| { // 8-bit string with 16bit shadow. |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<String> stringInstrumentedOwner("String"); |
| stringInstrumentedOwner.m_value.characters(); |
| helper.addRootObject(stringInstrumentedOwner); |
| EXPECT_EQ(sizeof(StringImpl) + stringInstrumentedOwner.m_value.length() * (sizeof(LChar) + sizeof(UChar)), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| } |
| |
| { // 16 bit string. |
| InstrumentationTestHelper helper; |
| String string("String"); |
| InstrumentedOwner<String> stringInstrumentedOwner(String(string.characters(), string.length())); |
| helper.addRootObject(stringInstrumentedOwner); |
| EXPECT_EQ(sizeof(StringImpl) + stringInstrumentedOwner.m_value.length() * sizeof(UChar), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| { // ASCIILiteral |
| InstrumentationTestHelper helper; |
| ASCIILiteral literal("String"); |
| InstrumentedOwner<String> stringInstrumentedOwner(literal); |
| helper.addRootObject(stringInstrumentedOwner); |
| EXPECT_EQ(sizeof(StringImpl), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| { // Zero terminated internal buffer. |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<String> stringInstrumentedOwner("string"); |
| stringInstrumentedOwner.m_value.charactersWithNullTermination(); |
| helper.addRootObject(stringInstrumentedOwner); |
| EXPECT_EQ(sizeof(StringImpl) + (stringInstrumentedOwner.m_value.length() + 1) * (sizeof(LChar) + sizeof(UChar)), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| } |
| |
| { // Substring |
| InstrumentationTestHelper helper; |
| String baseString("String"); |
| baseString.characters(); // Force 16 shadow creation. |
| InstrumentedOwner<String> stringInstrumentedOwner(baseString.substringSharingImpl(1, 4)); |
| helper.addRootObject(stringInstrumentedOwner); |
| EXPECT_EQ(sizeof(StringImpl) * 2 + baseString.length() * (sizeof(LChar) + sizeof(UChar)), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(3u, helper.visitedObjects()); |
| } |
| |
| { // Owned buffer. |
| InstrumentationTestHelper helper; |
| StringBuffer<LChar> buffer(6); |
| InstrumentedOwner<String> stringInstrumentedOwner(String::adopt(buffer)); |
| helper.addRootObject(stringInstrumentedOwner); |
| EXPECT_EQ(sizeof(StringImpl) + stringInstrumentedOwner.m_value.length(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| } |
| |
| { |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<AtomicString> atomicStringInstrumentedOwner("AtomicString"); |
| atomicStringInstrumentedOwner.m_value.string().characters(); // Force 16bit shadow creation. |
| helper.addRootObject(atomicStringInstrumentedOwner); |
| EXPECT_EQ(sizeof(StringImpl) + atomicStringInstrumentedOwner.m_value.length() * (sizeof(LChar) + sizeof(UChar)), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| } |
| |
| { |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<CString> cStringInstrumentedOwner("CString"); |
| helper.addRootObject(cStringInstrumentedOwner); |
| EXPECT_EQ(sizeof(WTF::CStringBuffer) + cStringInstrumentedOwner.m_value.length(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| } |
| |
| class TwoPointersToRefPtr { |
| public: |
| TwoPointersToRefPtr(const RefPtr<StringImpl>& value) : m_ptr1(&value), m_ptr2(&value) { } |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_ptr1); |
| info.addMember(m_ptr2); |
| } |
| |
| const RefPtr<StringImpl>* m_ptr1; |
| const RefPtr<StringImpl>* m_ptr2; |
| }; |
| |
| TEST(MemoryInstrumentationTest, refPtrPtr) |
| { |
| InstrumentationTestHelper helper; |
| RefPtr<StringImpl> refPtr; |
| TwoPointersToRefPtr root(refPtr); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(RefPtr<StringImpl>), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| class TwoPointersToOwnPtr { |
| public: |
| TwoPointersToOwnPtr(const OwnPtr<NotInstrumented>& value) : m_ptr1(&value), m_ptr2(&value) { } |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_ptr1); |
| info.addMember(m_ptr2); |
| } |
| |
| const OwnPtr<NotInstrumented>* m_ptr1; |
| const OwnPtr<NotInstrumented>* m_ptr2; |
| }; |
| |
| TEST(MemoryInstrumentationTest, ownPtrPtr) |
| { |
| InstrumentationTestHelper helper; |
| OwnPtr<NotInstrumented> ownPtr; |
| TwoPointersToOwnPtr root(ownPtr); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(OwnPtr<NotInstrumented>), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| template<typename T> |
| class InstrumentedTemplate { |
| public: |
| template<typename V> |
| InstrumentedTemplate(const V& value) : m_value(value) { } |
| |
| template<typename MemoryObjectInfo> |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| typename MemoryObjectInfo::ClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_value); |
| } |
| |
| T m_value; |
| }; |
| |
| TEST(MemoryInstrumentationTest, detectReportMemoryUsageMethod) |
| { |
| { |
| InstrumentationTestHelper helper; |
| |
| OwnPtr<InstrumentedTemplate<String> > value(adoptPtr(new InstrumentedTemplate<String>(""))); |
| InstrumentedOwner<InstrumentedTemplate<String>* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(InstrumentedTemplate<String>) + sizeof(StringImpl), helper.reportedSizeForAllTypes()); |
| // FIXME: it is failing on Chromium Canary bots but works fine locally. |
| // EXPECT_EQ(2, helper.visitedObjects()); |
| } |
| { |
| InstrumentationTestHelper helper; |
| |
| OwnPtr<InstrumentedTemplate<NotInstrumented> > value(adoptPtr(new InstrumentedTemplate<NotInstrumented>(""))); |
| InstrumentedOwner<InstrumentedTemplate<NotInstrumented>* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(InstrumentedTemplate<NotInstrumented>), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| } |
| |
| TEST(MemoryInstrumentationTest, vectorZeroInlineCapacity) |
| { |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<Vector<int> > vectorOwner(16); |
| helper.addRootObject(vectorOwner); |
| EXPECT_EQ(16 * sizeof(int), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, vectorFieldWithInlineCapacity) |
| { |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<Vector<int, 4> > vectorOwner; |
| helper.addRootObject(vectorOwner); |
| EXPECT_EQ(static_cast<size_t>(0), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(0u, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, vectorFieldWithInlineCapacityResized) |
| { |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<Vector<int, 4> > vectorOwner; |
| vectorOwner.m_value.reserveCapacity(8); |
| helper.addRootObject(vectorOwner); |
| EXPECT_EQ(8u * sizeof(int), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, heapAllocatedVectorWithInlineCapacity) |
| { |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<OwnPtr<Vector<int, 4> > > vectorOwner; |
| vectorOwner.m_value = adoptPtr(new Vector<int, 4>()); |
| helper.addRootObject(vectorOwner); |
| EXPECT_EQ(sizeof(Vector<int, 4>), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, heapAllocatedVectorWithInlineCapacityResized) |
| { |
| InstrumentationTestHelper helper; |
| InstrumentedOwner<OwnPtr<Vector<int, 4> > > vectorOwner; |
| vectorOwner.m_value = adoptPtr(new Vector<int, 4>()); |
| vectorOwner.m_value->reserveCapacity(8); |
| helper.addRootObject(vectorOwner); |
| EXPECT_EQ(8u * sizeof(int) + sizeof(Vector<int, 4>), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, vectorWithInstrumentedType) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef Vector<String> StringVector; |
| OwnPtr<StringVector> value = adoptPtr(new StringVector()); |
| size_t count = 10; |
| for (size_t i = 0; i < count; ++i) |
| value->append("string"); |
| InstrumentedOwner<StringVector* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(StringVector) + sizeof(String) * value->capacity() + (sizeof(StringImpl) + 6) * value->size(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(count + 2, (size_t)helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, hashSetWithInstrumentedType) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashSet<String> ValueType; |
| OwnPtr<ValueType> value = adoptPtr(new ValueType()); |
| size_t count = 10; |
| for (size_t i = 0; i < count; ++i) |
| value->add(String::number(i)); |
| InstrumentedOwner<ValueType* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(ValueType) + sizeof(String) * value->capacity() + (sizeof(StringImpl) + 1) * value->size(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(count + 1, (size_t)helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, hashMapWithNotInstrumentedKeysAndValues) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashMap<int, int> IntToIntMap; |
| OwnPtr<IntToIntMap> value = adoptPtr(new IntToIntMap()); |
| size_t count = 10; |
| for (size_t i = 1; i <= count; ++i) |
| value->set(i, i); |
| InstrumentedOwner<IntToIntMap* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(IntToIntMap) + sizeof(IntToIntMap::ValueType) * value->capacity(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1u, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, hashMapWithInstrumentedKeys) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashMap<String, int> StringToIntMap; |
| OwnPtr<StringToIntMap> value = adoptPtr(new StringToIntMap()); |
| size_t count = 10; |
| for (size_t i = 10; i < 10 + count; ++i) |
| value->set(String::number(i), i); |
| InstrumentedOwner<StringToIntMap* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(StringToIntMap) + sizeof(StringToIntMap::ValueType) * value->capacity() + (sizeof(StringImpl) + 2) * value->size(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(count + 1, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, hashMapWithInstrumentedValues) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashMap<int, String> IntToStringMap; |
| OwnPtr<IntToStringMap> value = adoptPtr(new IntToStringMap()); |
| size_t count = 10; |
| for (size_t i = 10; i < 10 + count; ++i) |
| value->set(i, String::number(i)); |
| InstrumentedOwner<IntToStringMap* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(IntToStringMap) + sizeof(IntToStringMap::ValueType) * value->capacity() + (sizeof(StringImpl) + 2) * value->size(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(count + 1, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, hashMapWithInstrumentedKeysAndValues) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashMap<String, String> StringToStringMap; |
| OwnPtr<StringToStringMap> value = adoptPtr(new StringToStringMap()); |
| size_t count = 10; |
| for (size_t i = 10; i < 10 + count; ++i) |
| value->set(String::number(count + i), String::number(i)); |
| InstrumentedOwner<StringToStringMap* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(StringToStringMap) + sizeof(StringToStringMap::ValueType) * value->capacity() + 2 * (sizeof(StringImpl) + 2) * value->size(), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u * count + 1, helper.visitedObjects()); |
| } |
| |
| class InstrumentedRefCounted : public RefCounted<InstrumentedRefCounted> { |
| public: |
| InstrumentedRefCounted() : m_notInstrumented(new NotInstrumented) { } |
| ~InstrumentedRefCounted() { delete m_notInstrumented; } |
| |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_notInstrumented, "m_notInstrumented", WTF::RetainingPointer); |
| } |
| private: |
| NotInstrumented* m_notInstrumented; |
| }; |
| |
| TEST(MemoryInstrumentationTest, hashMapWithInstrumentedPointerKeysAndPointerValues) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashMap<RefPtr<InstrumentedRefCounted>, RefPtr<InstrumentedRefCounted> > InstrumentedToInstrumentedMap; |
| OwnPtr<InstrumentedToInstrumentedMap> value(adoptPtr(new InstrumentedToInstrumentedMap())); |
| size_t count = 10; |
| for (size_t i = 0; i < count; ++i) |
| value->set(adoptRef(new InstrumentedRefCounted()), adoptRef(new InstrumentedRefCounted())); |
| InstrumentedOwner<InstrumentedToInstrumentedMap* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(InstrumentedToInstrumentedMap) |
| + sizeof(InstrumentedToInstrumentedMap::ValueType) * value->capacity() |
| + 2 * (sizeof(InstrumentedRefCounted) + sizeof(NotInstrumented)) * value->size(), |
| helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u * 2u * count + 1, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, listHashSetWithInstrumentedType) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef ListHashSet<String, 8> TestSet; |
| OwnPtr<TestSet> value = adoptPtr(new TestSet()); |
| size_t count = 10; |
| for (size_t i = 0; i < count; ++i) |
| value->add(String::number(i)); |
| InstrumentedOwner<TestSet* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(TestSet) + sizeof(String) * value->capacity() + (sizeof(StringImpl) + 1 * sizeof(LChar)) * count + |
| sizeof(WTF::ListHashSetNodeAllocator<String, 8>) + sizeof(WTF::ListHashSetNode<String, 8>) * (count - 8), |
| helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1 + count, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, listHashSetWithInstrumentedTypeAfterValuesRemoval) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef ListHashSet<String, 8> TestSet; |
| OwnPtr<TestSet> value = adoptPtr(new TestSet()); |
| size_t count = 20; |
| for (size_t i = 0; i < count; ++i) |
| value->add(String::number(i)); |
| // Remove 10 values, 8 of which were allocated in the internal buffer. |
| for (size_t i = 0; i < 10; ++i) |
| value->remove(String::number(i)); |
| InstrumentedOwner<TestSet* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(TestSet) + sizeof(String) * value->capacity() + (sizeof(StringImpl) + 2 * sizeof(LChar)) * (count - 10) + |
| sizeof(WTF::ListHashSetNodeAllocator<String, 8>) + sizeof(WTF::ListHashSetNode<String, 8>) * (count - 10), |
| helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(1 + (count - 10), helper.visitedObjects()); |
| } |
| |
| class InstrumentedConvertibleToInt : public RefCounted<InstrumentedConvertibleToInt> { |
| public: |
| InstrumentedConvertibleToInt() : m_notInstrumented(new NotInstrumented) { } |
| virtual ~InstrumentedConvertibleToInt() { delete m_notInstrumented; } |
| |
| virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_notInstrumented); |
| } |
| |
| operator int() const { return 2012; } |
| |
| NotInstrumented* m_notInstrumented; |
| }; |
| |
| // This test checks if reportMemoryUsage method will be called on a class |
| // that can be implicitly cast to int. Currently objects of such classes are |
| // treated as integers when they are stored in a HashMap by value and |
| // reportMemoryUsage will not be called on them. We may fix that later. |
| TEST(MemoryInstrumentationTest, hashMapWithValuesConvertibleToInt) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashMap<RefPtr<InstrumentedConvertibleToInt>, int> TestMap; |
| OwnPtr<TestMap> value(adoptPtr(new TestMap())); |
| size_t count = 10; |
| for (size_t i = 0; i < count; ++i) |
| value->set(adoptRef(new InstrumentedConvertibleToInt()), 0); |
| InstrumentedOwner<TestMap* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(TestMap) + sizeof(TestMap::ValueType) * value->capacity() + |
| sizeof(InstrumentedConvertibleToInt) * count /* + sizeof(NotInstrumented) * count */, helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(count + 1, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, hashMapWithEnumKeysAndInstrumentedValues) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashMap<TestEnum, String> EnumToStringMap; |
| OwnPtr<EnumToStringMap> value(adoptPtr(new EnumToStringMap())); |
| size_t count = MY_ENUM_MAX; |
| for (size_t i = ONE; i <= count; ++i) |
| value->set(static_cast<TestEnum>(i), String::number(i)); |
| InstrumentedOwner<EnumToStringMap* > root(value.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(EnumToStringMap) |
| + sizeof(EnumToStringMap::ValueType) * value->capacity() |
| + (sizeof(StringImpl) + 1) * value->size(), |
| helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(count + 1, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, hashCountedSetWithInstrumentedValues) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef HashCountedSet<RefPtr<InstrumentedRefCounted> > TestSet; |
| OwnPtr<TestSet> set(adoptPtr(new TestSet())); |
| size_t count = 10; |
| for (size_t i = 0; i < count; ++i) { |
| RefPtr<InstrumentedRefCounted> instrumentedRefCounted = adoptRef(new InstrumentedRefCounted()); |
| for (size_t j = 0; j <= i; j++) |
| set->add(instrumentedRefCounted); |
| } |
| InstrumentedOwner<TestSet* > root(set.get()); |
| helper.addRootObject(root); |
| EXPECT_EQ(sizeof(TestSet) |
| + sizeof(HashMap<RefPtr<InstrumentedRefCounted>, unsigned>::ValueType) * set->capacity() |
| + (sizeof(InstrumentedRefCounted) + sizeof(NotInstrumented)) * set->size(), |
| helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u * count + 1, helper.visitedObjects()); |
| } |
| |
| TEST(MemoryInstrumentationTest, arrayBuffer) |
| { |
| InstrumentationTestHelper helper; |
| |
| typedef InstrumentedTemplate<RefPtr<ArrayBuffer> > ValueType; |
| ValueType value(ArrayBuffer::create(1000, sizeof(int))); |
| helper.addRootObject(value); |
| EXPECT_EQ(sizeof(int) * 1000 + sizeof(ArrayBuffer), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| EXPECT_EQ(2u, helper.linksCount(WTF::RetainingPointer)); |
| } |
| |
| class AncestorWithVirtualMethod { |
| public: |
| virtual char* data() { return m_data; } |
| |
| private: |
| char m_data[10]; |
| }; |
| |
| class ClassWithTwoAncestors : public AncestorWithVirtualMethod, public Instrumented { |
| public: |
| virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| Instrumented::reportMemoryUsage(memoryObjectInfo); |
| } |
| }; |
| |
| TEST(MemoryInstrumentationTest, instrumentedWithMultipleAncestors) |
| { |
| InstrumentationTestHelper helper; |
| OwnPtr<ClassWithTwoAncestors> instance = adoptPtr(new ClassWithTwoAncestors()); |
| ClassWithTwoAncestors* descendantPointer = instance.get(); |
| InstrumentedOwner<ClassWithTwoAncestors*> descendantPointerOwner(descendantPointer); |
| Instrumented* ancestorPointer = descendantPointer; |
| InstrumentedOwner<Instrumented*> ancestorPointerOwner(ancestorPointer); |
| EXPECT_NE(static_cast<void*>(ancestorPointer), static_cast<void*>(descendantPointer)); |
| |
| helper.addRootObject(descendantPointerOwner); |
| helper.addRootObject(ancestorPointerOwner); |
| EXPECT_EQ(sizeof(ClassWithTwoAncestors) + sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(3u, helper.visitedObjects()); |
| } |
| |
| class CheckCountedObjectsClient : public MemoryInstrumentationTestClient { |
| public: |
| CheckCountedObjectsClient(const void* expectedPointer) : m_expectedPointer(expectedPointer), m_expectedPointerFound(false) { } |
| virtual bool checkCountedObject(const void* pointer) |
| { |
| EXPECT_EQ(pointer, m_expectedPointer); |
| m_expectedPointerFound = true; |
| return true; |
| } |
| bool expectedPointerFound() { return m_expectedPointerFound; } |
| |
| private: |
| const void* m_expectedPointer; |
| bool m_expectedPointerFound; |
| }; |
| |
| TEST(MemoryInstrumentationTest, checkCountedObjectWithMultipleAncestors) |
| { |
| OwnPtr<ClassWithTwoAncestors> instance = adoptPtr(new ClassWithTwoAncestors()); |
| instance->disposeOwnedObject(); |
| ClassWithTwoAncestors* descendantPointer = instance.get(); |
| InstrumentedOwner<ClassWithTwoAncestors*> descendantPointerOwner(descendantPointer); |
| Instrumented* ancestorPointer = descendantPointer; |
| InstrumentedOwner<Instrumented*> ancestorPointerOwner(ancestorPointer); |
| EXPECT_NE(static_cast<void*>(ancestorPointer), static_cast<void*>(descendantPointer)); |
| |
| CheckCountedObjectsClient client(instance.get()); |
| InstrumentationTestImpl instrumentation(&client); |
| instrumentation.addRootObject(descendantPointerOwner); |
| instrumentation.addRootObject(ancestorPointerOwner); |
| EXPECT_TRUE(client.expectedPointerFound()); |
| } |
| |
| class TwoPointersToSameInsrumented { |
| public: |
| TwoPointersToSameInsrumented() |
| : m_ownPtr(adoptPtr(new ClassWithTwoAncestors())) |
| , m_baseClassPtr(m_ownPtr.get()) |
| { |
| EXPECT_NE(static_cast<void*>(m_ownPtr.get()), static_cast<void*>(m_baseClassPtr)); |
| } |
| void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const |
| { |
| MemoryClassInfo info(memoryObjectInfo, this, TestType); |
| info.addMember(m_ownPtr); |
| info.addMember(m_baseClassPtr); |
| } |
| |
| private: |
| OwnPtr<ClassWithTwoAncestors> m_ownPtr; |
| Instrumented* m_baseClassPtr; |
| }; |
| |
| class CountLinksFromInstrumentedObject : public MemoryInstrumentationTestClient { |
| public: |
| CountLinksFromInstrumentedObject() : m_linkCount(0) { } |
| virtual void reportEdge(const void*, const char* name, MemberType) OVERRIDE |
| { |
| if (name && !strcmp("m_notInstrumented", name)) |
| m_linkCount++; |
| } |
| int linkCount() const { return m_linkCount; } |
| |
| private: |
| int m_linkCount; |
| }; |
| |
| |
| TEST(MemoryInstrumentationTest, doNotReportEdgeTwice) |
| { |
| OwnPtr<TwoPointersToSameInsrumented> instance = adoptPtr(new TwoPointersToSameInsrumented()); |
| |
| CountLinksFromInstrumentedObject client; |
| InstrumentationTestImpl instrumentation(&client); |
| instrumentation.addRootObject(instance.get()); |
| EXPECT_EQ(1, client.linkCount()); |
| } |
| |
| class DerivedClass : public Instrumented { |
| public: |
| size_t m_member; |
| }; |
| |
| TEST(MemoryInstrumentationTest, detectBaseClassInstrumentation) |
| { |
| OwnPtr<DerivedClass> instance = adoptPtr(new DerivedClass()); |
| |
| InstrumentationTestHelper helper; |
| helper.addRootObject(instance.get(), TestType); |
| EXPECT_EQ(sizeof(Instrumented) + sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); |
| EXPECT_EQ(2u, helper.visitedObjects()); |
| } |
| |
| } // namespace |