/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { enum TestEnum { ONE = 1, TWO, THREE, MY_ENUM_MAX }; } namespace WTF { template<> struct DefaultHash { typedef IntHash Hash; }; template<> struct HashTraits : GenericHashTraits { static const bool emptyValueIsZero = true; static const bool needsDestruction = false; static void constructDeletedValue(TestEnum& slot) { slot = static_cast(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; MemoryObjectType TestType = "TestType"; class MemoryInstrumentationTestClient : public WTF::MemoryInstrumentationClient { public: 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; } 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 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 TypeToSizeMap; TypeToSizeMap m_totalSizes; WTF::HashSet m_visitedObjects; }; class InstrumentationTestImpl : public WTF::MemoryInstrumentation { public: explicit InstrumentationTestImpl(MemoryInstrumentationTestClient* client) : MemoryInstrumentation(client) , m_client(client) { } virtual void processDeferredInstrumentedPointers(); virtual void deferInstrumentedPointer(PassOwnPtr); 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); } private: MemoryInstrumentationTestClient* m_client; Vector > m_deferredInstrumentedPointers; }; class InstrumentationTestHelper : public InstrumentationTestImpl { public: InstrumentationTestHelper() : InstrumentationTestImpl(&m_client) { } private: MemoryInstrumentationTestClient m_client; }; void InstrumentationTestImpl::processDeferredInstrumentedPointers() { while (!m_deferredInstrumentedPointers.isEmpty()) { OwnPtr pointer = m_deferredInstrumentedPointers.last().release(); m_deferredInstrumentedPointers.removeLast(); pointer->process(this); } } void InstrumentationTestImpl::deferInstrumentedPointer(PassOwnPtr pointer) { m_deferredInstrumentedPointers.append(pointer); } class NotInstrumented { public: NotInstrumented(const char* = 0) { } char m_data[42]; }; class Instrumented { public: Instrumented() : m_notInstrumented(new NotInstrumented) { } virtual ~Instrumented() { delete m_notInstrumented; } virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const { MemoryClassInfo info(memoryObjectInfo, this, TestType); info.addMember(m_notInstrumented); } NotInstrumented* m_notInstrumented; }; TEST(MemoryInstrumentationTest, sizeOf) { InstrumentationTestHelper helper; Instrumented instrumented; helper.addRootObject(instrumented); EXPECT_EQ(sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); EXPECT_EQ(1u, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, nullCheck) { InstrumentationTestHelper helper; Instrumented* instrumented = 0; helper.addRootObject(instrumented); EXPECT_EQ(0u, helper.reportedSizeForAllTypes()); EXPECT_EQ(0u, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, ptrVsRef) { { InstrumentationTestHelper helper; Instrumented instrumented; helper.addRootObject(&instrumented); EXPECT_EQ(sizeof(Instrumented) + sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); EXPECT_EQ(2u, helper.visitedObjects()); } { InstrumentationTestHelper helper; Instrumented instrumented; helper.addRootObject(instrumented); EXPECT_EQ(sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); EXPECT_EQ(1u, helper.visitedObjects()); } } TEST(MemoryInstrumentationTest, ownPtr) { InstrumentationTestHelper helper; OwnPtr instrumented(adoptPtr(new Instrumented)); helper.addRootObject(instrumented); EXPECT_EQ(sizeof(Instrumented) + sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); EXPECT_EQ(2u, helper.visitedObjects()); } class InstrumentedRefPtr : public RefCounted { public: InstrumentedRefPtr() : m_notInstrumented(new NotInstrumented) { } virtual ~InstrumentedRefPtr() { delete m_notInstrumented; } static PassRefPtr create() { return adoptRef(new InstrumentedRefPtr()); } virtual void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const { MemoryClassInfo info(memoryObjectInfo, this, TestType); info.addMember(m_notInstrumented); } NotInstrumented* m_notInstrumented; }; TEST(MemoryInstrumentationTest, refPtr) { InstrumentationTestHelper helper; RefPtr instrumentedRefPtr(adoptRef(new InstrumentedRefPtr)); helper.addRootObject(instrumentedRefPtr); EXPECT_EQ(sizeof(InstrumentedRefPtr) + sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); EXPECT_EQ(2u, helper.visitedObjects()); } 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 m_notInstrumentedOwnPtr; }; TEST(MemoryInstrumentationTest, ownPtrNotInstrumented) { InstrumentationTestHelper helper; InstrumentedWithOwnPtr instrumentedWithOwnPtr; helper.addRootObject(instrumentedWithOwnPtr); EXPECT_EQ(2u * sizeof(NotInstrumented), helper.reportedSizeForAllTypes()); EXPECT_EQ(2u, helper.visitedObjects()); } 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 m_instrumentedUndefined; }; TEST(MemoryInstrumentationTest, ownerTypePropagation) { InstrumentationTestHelper helper; OwnPtr instrumentedDOM(adoptPtr(new InstrumentedDOM)); helper.addRootObject(instrumentedDOM); 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 class InstrumentedOwner { public: template InstrumentedOwner(const V& value) : m_value(value) { } InstrumentedOwner() { } void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const { MemoryClassInfo info(memoryObjectInfo, this, TestType); info.addMember(m_value); } T m_value; }; TEST(MemoryInstrumentationTest, visitStrings) { { // 8-bit string. InstrumentationTestHelper helper; InstrumentedOwner 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 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 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 stringInstrumentedOwner(literal); helper.addRootObject(stringInstrumentedOwner); EXPECT_EQ(sizeof(StringImpl), helper.reportedSizeForAllTypes()); EXPECT_EQ(1u, helper.visitedObjects()); } { // Zero terminated internal buffer. InstrumentationTestHelper helper; InstrumentedOwner 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 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 buffer(6); InstrumentedOwner 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 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 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& 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* m_ptr1; const RefPtr* m_ptr2; }; TEST(MemoryInstrumentationTest, refPtrPtr) { InstrumentationTestHelper helper; RefPtr refPtr; TwoPointersToRefPtr root(refPtr); helper.addRootObject(root); EXPECT_EQ(sizeof(RefPtr), helper.reportedSizeForAllTypes()); EXPECT_EQ(1u, helper.visitedObjects()); } class TwoPointersToOwnPtr { public: TwoPointersToOwnPtr(const OwnPtr& 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* m_ptr1; const OwnPtr* m_ptr2; }; TEST(MemoryInstrumentationTest, ownPtrPtr) { InstrumentationTestHelper helper; OwnPtr ownPtr; TwoPointersToOwnPtr root(ownPtr); helper.addRootObject(root); EXPECT_EQ(sizeof(OwnPtr), helper.reportedSizeForAllTypes()); EXPECT_EQ(1u, helper.visitedObjects()); } template class InstrumentedTemplate { public: template InstrumentedTemplate(const V& value) : m_value(value) { } template 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 > value(adoptPtr(new InstrumentedTemplate(""))); InstrumentedOwner* > root(value.get()); helper.addRootObject(root); EXPECT_EQ(sizeof(InstrumentedTemplate) + sizeof(StringImpl), helper.reportedSizeForAllTypes()); // FIXME: it is failing on Chromium Canary bots but works fine locally. // EXPECT_EQ(2, helper.visitedObjects()); } { InstrumentationTestHelper helper; OwnPtr > value(adoptPtr(new InstrumentedTemplate(""))); InstrumentedOwner* > root(value.get()); helper.addRootObject(root); EXPECT_EQ(sizeof(InstrumentedTemplate), helper.reportedSizeForAllTypes()); EXPECT_EQ(1u, helper.visitedObjects()); } } TEST(MemoryInstrumentationTest, vectorZeroInlineCapacity) { InstrumentationTestHelper helper; InstrumentedOwner > vectorOwner(16); helper.addRootObject(vectorOwner); EXPECT_EQ(16 * sizeof(int), helper.reportedSizeForAllTypes()); EXPECT_EQ(1u, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, vectorFieldWithInlineCapacity) { InstrumentationTestHelper helper; InstrumentedOwner > vectorOwner; helper.addRootObject(vectorOwner); EXPECT_EQ(static_cast(0), helper.reportedSizeForAllTypes()); EXPECT_EQ(0u, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, vectorFieldWithInlineCapacityResized) { InstrumentationTestHelper helper; InstrumentedOwner > 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 > > vectorOwner; vectorOwner.m_value = adoptPtr(new Vector()); helper.addRootObject(vectorOwner); EXPECT_EQ(sizeof(Vector), helper.reportedSizeForAllTypes()); EXPECT_EQ(1u, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, heapAllocatedVectorWithInlineCapacityResized) { InstrumentationTestHelper helper; InstrumentedOwner > > vectorOwner; vectorOwner.m_value = adoptPtr(new Vector()); vectorOwner.m_value->reserveCapacity(8); helper.addRootObject(vectorOwner); EXPECT_EQ(8u * sizeof(int) + sizeof(Vector), helper.reportedSizeForAllTypes()); EXPECT_EQ(2u, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, vectorWithInstrumentedType) { InstrumentationTestHelper helper; typedef Vector StringVector; OwnPtr value = adoptPtr(new StringVector()); size_t count = 10; for (size_t i = 0; i < count; ++i) value->append("string"); InstrumentedOwner 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 ValueType; OwnPtr value = adoptPtr(new ValueType()); size_t count = 10; for (size_t i = 0; i < count; ++i) value->add(String::number(i)); InstrumentedOwner 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 IntToIntMap; OwnPtr value = adoptPtr(new IntToIntMap()); size_t count = 10; for (size_t i = 1; i <= count; ++i) value->set(i, i); InstrumentedOwner 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 StringToIntMap; OwnPtr value = adoptPtr(new StringToIntMap()); size_t count = 10; for (size_t i = 10; i < 10 + count; ++i) value->set(String::number(i), i); InstrumentedOwner 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 IntToStringMap; OwnPtr value = adoptPtr(new IntToStringMap()); size_t count = 10; for (size_t i = 10; i < 10 + count; ++i) value->set(i, String::number(i)); InstrumentedOwner 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 StringToStringMap; OwnPtr 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 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()); } TEST(MemoryInstrumentationTest, hashMapWithInstrumentedPointerKeysAndPointerValues) { InstrumentationTestHelper helper; typedef HashMap InstrumentedToInstrumentedMap; OwnPtr value(adoptPtr(new InstrumentedToInstrumentedMap())); Vector > valuesVector; size_t count = 10; for (size_t i = 0; i < count; ++i) { valuesVector.append(adoptPtr(new Instrumented())); valuesVector.append(adoptPtr(new Instrumented())); value->set(valuesVector[2 * i].get(), valuesVector[2 * i + 1].get()); } InstrumentedOwner root(value.get()); helper.addRootObject(root); EXPECT_EQ(sizeof(InstrumentedToInstrumentedMap) + sizeof(InstrumentedToInstrumentedMap::ValueType) * value->capacity() + 2 * (sizeof(Instrumented) + sizeof(NotInstrumented)) * value->size(), helper.reportedSizeForAllTypes()); EXPECT_EQ(2u * 2u * count + 1, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, listHashSetWithInstrumentedType) { InstrumentationTestHelper helper; typedef ListHashSet TestSet; OwnPtr value = adoptPtr(new TestSet()); size_t count = 10; for (size_t i = 0; i < count; ++i) value->add(String::number(i)); InstrumentedOwner root(value.get()); helper.addRootObject(root); EXPECT_EQ(sizeof(TestSet) + sizeof(String) * value->capacity() + (sizeof(StringImpl) + 1 * sizeof(LChar)) * count + sizeof(WTF::ListHashSetNodeAllocator) + sizeof(WTF::ListHashSetNode) * (count - 8), helper.reportedSizeForAllTypes()); EXPECT_EQ(1 + count, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, listHashSetWithInstrumentedTypeAfterValuesRemoval) { InstrumentationTestHelper helper; typedef ListHashSet TestSet; OwnPtr 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 root(value.get()); helper.addRootObject(root); EXPECT_EQ(sizeof(TestSet) + sizeof(String) * value->capacity() + (sizeof(StringImpl) + 2 * sizeof(LChar)) * (count - 10) + sizeof(WTF::ListHashSetNodeAllocator) + sizeof(WTF::ListHashSetNode) * (count - 10), helper.reportedSizeForAllTypes()); EXPECT_EQ(1 + (count - 10), helper.visitedObjects()); } class InstrumentedConvertibleToInt { public: InstrumentedConvertibleToInt() : m_notInstrumented(0) { } virtual ~InstrumentedConvertibleToInt() { } 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 TestMap; OwnPtr value(adoptPtr(new TestMap())); Vector > keysVector; Vector > valuesVector; size_t count = 10; for (size_t i = 0; i < count; ++i) { keysVector.append(adoptPtr(new InstrumentedConvertibleToInt())); valuesVector.append(adoptPtr(new NotInstrumented())); value->set(keysVector[i].get(), InstrumentedConvertibleToInt()).iterator->value.m_notInstrumented = valuesVector[i].get(); } InstrumentedOwner 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 EnumToStringMap; OwnPtr value(adoptPtr(new EnumToStringMap())); size_t count = MY_ENUM_MAX; for (size_t i = ONE; i <= count; ++i) value->set(static_cast(i), String::number(i)); InstrumentedOwner 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 TestSet; OwnPtr set(adoptPtr(new TestSet())); Vector > keysVector; size_t count = 10; for (size_t i = 0; i < count; ++i) { keysVector.append(adoptPtr(new Instrumented())); for (size_t j = 0; j <= i; j++) set->add(keysVector.last().get()); } InstrumentedOwner root(set.get()); helper.addRootObject(root); EXPECT_EQ(sizeof(TestSet) + sizeof(HashMap::ValueType) * set->capacity() + (sizeof(Instrumented) + sizeof(NotInstrumented)) * set->size(), helper.reportedSizeForAllTypes()); EXPECT_EQ(2u * count + 1, helper.visitedObjects()); } TEST(MemoryInstrumentationTest, arrayBuffer) { InstrumentationTestHelper helper; typedef InstrumentedTemplate > 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()); } 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); } }; TEST(MemoryInstrumentationTest, instrumentedWithMultipleAncestors) { InstrumentationTestHelper helper; OwnPtr instance = adoptPtr(new ClassWithTwoAncestors()); ClassWithTwoAncestors* descendantPointer = instance.get(); InstrumentedOwner descendantPointerOwner(descendantPointer); Instrumented* ancestorPointer = descendantPointer; InstrumentedOwner ancestorPointerOwner(ancestorPointer); EXPECT_NE(static_cast(ancestorPointer), static_cast(descendantPointer)); helper.addRootObject(descendantPointerOwner); helper.addRootObject(ancestorPointerOwner); EXPECT_EQ(sizeof(ClassWithTwoAncestors), helper.reportedSizeForAllTypes()); EXPECT_EQ(2u, 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 instance = adoptPtr(new ClassWithTwoAncestors()); ClassWithTwoAncestors* descendantPointer = instance.get(); InstrumentedOwner descendantPointerOwner(descendantPointer); Instrumented* ancestorPointer = descendantPointer; InstrumentedOwner ancestorPointerOwner(ancestorPointer); EXPECT_NE(static_cast(ancestorPointer), static_cast(descendantPointer)); CheckCountedObjectsClient client(instance.get()); InstrumentationTestImpl instrumentation(&client); instrumentation.addRootObject(descendantPointerOwner); instrumentation.addRootObject(ancestorPointerOwner); EXPECT_TRUE(client.expectedPointerFound()); } } // namespace