indirect_reference_table_test.cc revision 28bd2e4f151267b34b8e1eb19c489d8d547bbf5c
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "indirect_reference_table-inl.h" 18 19#include "class_linker-inl.h" 20#include "common_runtime_test.h" 21#include "mirror/object-inl.h" 22#include "scoped_thread_state_change-inl.h" 23 24namespace art { 25 26class IndirectReferenceTableTest : public CommonRuntimeTest {}; 27 28static void CheckDump(IndirectReferenceTable* irt, size_t num_objects, size_t num_unique) 29 REQUIRES_SHARED(Locks::mutator_lock_) { 30 std::ostringstream oss; 31 irt->Dump(oss); 32 if (num_objects == 0) { 33 EXPECT_EQ(oss.str().find("java.lang.Object"), std::string::npos) << oss.str(); 34 } else if (num_objects == 1) { 35 EXPECT_NE(oss.str().find("1 of java.lang.Object"), std::string::npos) << oss.str(); 36 } else { 37 EXPECT_NE(oss.str().find(StringPrintf("%zd of java.lang.Object (%zd unique instances)", 38 num_objects, num_unique)), 39 std::string::npos) 40 << "\n Expected number of objects: " << num_objects 41 << "\n Expected unique objects: " << num_unique << "\n" 42 << oss.str(); 43 } 44} 45 46TEST_F(IndirectReferenceTableTest, BasicTest) { 47 // This will lead to error messages in the log. 48 ScopedLogSeverity sls(LogSeverity::FATAL); 49 50 ScopedObjectAccess soa(Thread::Current()); 51 static const size_t kTableInitial = 10; 52 static const size_t kTableMax = 20; 53 IndirectReferenceTable irt(kTableInitial, kTableMax, kGlobal); 54 55 mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;"); 56 StackHandleScope<4> hs(soa.Self()); 57 ASSERT_TRUE(c != nullptr); 58 Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self())); 59 ASSERT_TRUE(obj0.Get() != nullptr); 60 Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self())); 61 ASSERT_TRUE(obj1.Get() != nullptr); 62 Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self())); 63 ASSERT_TRUE(obj2.Get() != nullptr); 64 Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self())); 65 ASSERT_TRUE(obj3.Get() != nullptr); 66 67 const uint32_t cookie = IRT_FIRST_SEGMENT; 68 69 CheckDump(&irt, 0, 0); 70 71 IndirectRef iref0 = (IndirectRef) 0x11110; 72 EXPECT_FALSE(irt.Remove(cookie, iref0)) << "unexpectedly successful removal"; 73 74 // Add three, check, remove in the order in which they were added. 75 iref0 = irt.Add(cookie, obj0.Get()); 76 EXPECT_TRUE(iref0 != nullptr); 77 CheckDump(&irt, 1, 1); 78 IndirectRef iref1 = irt.Add(cookie, obj1.Get()); 79 EXPECT_TRUE(iref1 != nullptr); 80 CheckDump(&irt, 2, 2); 81 IndirectRef iref2 = irt.Add(cookie, obj2.Get()); 82 EXPECT_TRUE(iref2 != nullptr); 83 CheckDump(&irt, 3, 3); 84 85 EXPECT_OBJ_PTR_EQ(obj0.Get(), irt.Get(iref0)); 86 EXPECT_OBJ_PTR_EQ(obj1.Get(), irt.Get(iref1)); 87 EXPECT_OBJ_PTR_EQ(obj2.Get(), irt.Get(iref2)); 88 89 EXPECT_TRUE(irt.Remove(cookie, iref0)); 90 CheckDump(&irt, 2, 2); 91 EXPECT_TRUE(irt.Remove(cookie, iref1)); 92 CheckDump(&irt, 1, 1); 93 EXPECT_TRUE(irt.Remove(cookie, iref2)); 94 CheckDump(&irt, 0, 0); 95 96 // Table should be empty now. 97 EXPECT_EQ(0U, irt.Capacity()); 98 99 // Get invalid entry (off the end of the list). 100 EXPECT_TRUE(irt.Get(iref0) == nullptr); 101 102 // Add three, remove in the opposite order. 103 iref0 = irt.Add(cookie, obj0.Get()); 104 EXPECT_TRUE(iref0 != nullptr); 105 iref1 = irt.Add(cookie, obj1.Get()); 106 EXPECT_TRUE(iref1 != nullptr); 107 iref2 = irt.Add(cookie, obj2.Get()); 108 EXPECT_TRUE(iref2 != nullptr); 109 CheckDump(&irt, 3, 3); 110 111 ASSERT_TRUE(irt.Remove(cookie, iref2)); 112 CheckDump(&irt, 2, 2); 113 ASSERT_TRUE(irt.Remove(cookie, iref1)); 114 CheckDump(&irt, 1, 1); 115 ASSERT_TRUE(irt.Remove(cookie, iref0)); 116 CheckDump(&irt, 0, 0); 117 118 // Table should be empty now. 119 ASSERT_EQ(0U, irt.Capacity()); 120 121 // Add three, remove middle / middle / bottom / top. (Second attempt 122 // to remove middle should fail.) 123 iref0 = irt.Add(cookie, obj0.Get()); 124 EXPECT_TRUE(iref0 != nullptr); 125 iref1 = irt.Add(cookie, obj1.Get()); 126 EXPECT_TRUE(iref1 != nullptr); 127 iref2 = irt.Add(cookie, obj2.Get()); 128 EXPECT_TRUE(iref2 != nullptr); 129 CheckDump(&irt, 3, 3); 130 131 ASSERT_EQ(3U, irt.Capacity()); 132 133 ASSERT_TRUE(irt.Remove(cookie, iref1)); 134 CheckDump(&irt, 2, 2); 135 ASSERT_FALSE(irt.Remove(cookie, iref1)); 136 CheckDump(&irt, 2, 2); 137 138 // Get invalid entry (from hole). 139 EXPECT_TRUE(irt.Get(iref1) == nullptr); 140 141 ASSERT_TRUE(irt.Remove(cookie, iref2)); 142 CheckDump(&irt, 1, 1); 143 ASSERT_TRUE(irt.Remove(cookie, iref0)); 144 CheckDump(&irt, 0, 0); 145 146 // Table should be empty now. 147 ASSERT_EQ(0U, irt.Capacity()); 148 149 // Add four entries. Remove #1, add new entry, verify that table size 150 // is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify 151 // that we delete one and don't hole-compact the other. 152 iref0 = irt.Add(cookie, obj0.Get()); 153 EXPECT_TRUE(iref0 != nullptr); 154 iref1 = irt.Add(cookie, obj1.Get()); 155 EXPECT_TRUE(iref1 != nullptr); 156 iref2 = irt.Add(cookie, obj2.Get()); 157 EXPECT_TRUE(iref2 != nullptr); 158 IndirectRef iref3 = irt.Add(cookie, obj3.Get()); 159 EXPECT_TRUE(iref3 != nullptr); 160 CheckDump(&irt, 4, 4); 161 162 ASSERT_TRUE(irt.Remove(cookie, iref1)); 163 CheckDump(&irt, 3, 3); 164 165 iref1 = irt.Add(cookie, obj1.Get()); 166 EXPECT_TRUE(iref1 != nullptr); 167 168 ASSERT_EQ(4U, irt.Capacity()) << "hole not filled"; 169 CheckDump(&irt, 4, 4); 170 171 ASSERT_TRUE(irt.Remove(cookie, iref1)); 172 CheckDump(&irt, 3, 3); 173 ASSERT_TRUE(irt.Remove(cookie, iref3)); 174 CheckDump(&irt, 2, 2); 175 176 ASSERT_EQ(3U, irt.Capacity()) << "should be 3 after two deletions"; 177 178 ASSERT_TRUE(irt.Remove(cookie, iref2)); 179 CheckDump(&irt, 1, 1); 180 ASSERT_TRUE(irt.Remove(cookie, iref0)); 181 CheckDump(&irt, 0, 0); 182 183 ASSERT_EQ(0U, irt.Capacity()) << "not empty after split remove"; 184 185 // Add an entry, remove it, add a new entry, and try to use the original 186 // iref. They have the same slot number but are for different objects. 187 // With the extended checks in place, this should fail. 188 iref0 = irt.Add(cookie, obj0.Get()); 189 EXPECT_TRUE(iref0 != nullptr); 190 CheckDump(&irt, 1, 1); 191 ASSERT_TRUE(irt.Remove(cookie, iref0)); 192 CheckDump(&irt, 0, 0); 193 iref1 = irt.Add(cookie, obj1.Get()); 194 EXPECT_TRUE(iref1 != nullptr); 195 CheckDump(&irt, 1, 1); 196 ASSERT_FALSE(irt.Remove(cookie, iref0)) << "mismatched del succeeded"; 197 CheckDump(&irt, 1, 1); 198 ASSERT_TRUE(irt.Remove(cookie, iref1)) << "switched del failed"; 199 ASSERT_EQ(0U, irt.Capacity()) << "switching del not empty"; 200 CheckDump(&irt, 0, 0); 201 202 // Same as above, but with the same object. A more rigorous checker 203 // (e.g. with slot serialization) will catch this. 204 iref0 = irt.Add(cookie, obj0.Get()); 205 EXPECT_TRUE(iref0 != nullptr); 206 CheckDump(&irt, 1, 1); 207 ASSERT_TRUE(irt.Remove(cookie, iref0)); 208 CheckDump(&irt, 0, 0); 209 iref1 = irt.Add(cookie, obj0.Get()); 210 EXPECT_TRUE(iref1 != nullptr); 211 CheckDump(&irt, 1, 1); 212 if (iref0 != iref1) { 213 // Try 0, should not work. 214 ASSERT_FALSE(irt.Remove(cookie, iref0)) << "temporal del succeeded"; 215 } 216 ASSERT_TRUE(irt.Remove(cookie, iref1)) << "temporal cleanup failed"; 217 ASSERT_EQ(0U, irt.Capacity()) << "temporal del not empty"; 218 CheckDump(&irt, 0, 0); 219 220 // null isn't a valid iref. 221 ASSERT_TRUE(irt.Get(nullptr) == nullptr); 222 223 // Stale lookup. 224 iref0 = irt.Add(cookie, obj0.Get()); 225 EXPECT_TRUE(iref0 != nullptr); 226 CheckDump(&irt, 1, 1); 227 ASSERT_TRUE(irt.Remove(cookie, iref0)); 228 EXPECT_TRUE(irt.Get(iref0) == nullptr) << "stale lookup succeeded"; 229 CheckDump(&irt, 0, 0); 230 231 // Test table resizing. 232 // These ones fit... 233 IndirectRef manyRefs[kTableInitial]; 234 for (size_t i = 0; i < kTableInitial; i++) { 235 manyRefs[i] = irt.Add(cookie, obj0.Get()); 236 ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i; 237 CheckDump(&irt, i + 1, 1); 238 } 239 // ...this one causes overflow. 240 iref0 = irt.Add(cookie, obj0.Get()); 241 ASSERT_TRUE(iref0 != nullptr); 242 ASSERT_EQ(kTableInitial + 1, irt.Capacity()); 243 CheckDump(&irt, kTableInitial + 1, 1); 244 245 for (size_t i = 0; i < kTableInitial; i++) { 246 ASSERT_TRUE(irt.Remove(cookie, manyRefs[i])) << "failed removing " << i; 247 CheckDump(&irt, kTableInitial - i, 1); 248 } 249 // Because of removal order, should have 11 entries, 10 of them holes. 250 ASSERT_EQ(kTableInitial + 1, irt.Capacity()); 251 252 ASSERT_TRUE(irt.Remove(cookie, iref0)) << "multi-remove final failed"; 253 254 ASSERT_EQ(0U, irt.Capacity()) << "multi-del not empty"; 255 CheckDump(&irt, 0, 0); 256} 257 258} // namespace art 259