indirect_reference_table_test.cc revision 9d7ef62b854289632791a83223c1a5a5b3c8fc64
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 kTableMax = 20;
52  std::string error_msg;
53  IndirectReferenceTable irt(kTableMax,
54                             kGlobal,
55                             IndirectReferenceTable::ResizableCapacity::kNo,
56                             &error_msg);
57  ASSERT_TRUE(irt.IsValid()) << error_msg;
58
59  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
60  StackHandleScope<4> hs(soa.Self());
61  ASSERT_TRUE(c != nullptr);
62  Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
63  ASSERT_TRUE(obj0.Get() != nullptr);
64  Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
65  ASSERT_TRUE(obj1.Get() != nullptr);
66  Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
67  ASSERT_TRUE(obj2.Get() != nullptr);
68  Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
69  ASSERT_TRUE(obj3.Get() != nullptr);
70
71  const IRTSegmentState cookie = kIRTFirstSegment;
72
73  CheckDump(&irt, 0, 0);
74
75  IndirectRef iref0 = (IndirectRef) 0x11110;
76  EXPECT_FALSE(irt.Remove(cookie, iref0)) << "unexpectedly successful removal";
77
78  // Add three, check, remove in the order in which they were added.
79  iref0 = irt.Add(cookie, obj0.Get());
80  EXPECT_TRUE(iref0 != nullptr);
81  CheckDump(&irt, 1, 1);
82  IndirectRef iref1 = irt.Add(cookie, obj1.Get());
83  EXPECT_TRUE(iref1 != nullptr);
84  CheckDump(&irt, 2, 2);
85  IndirectRef iref2 = irt.Add(cookie, obj2.Get());
86  EXPECT_TRUE(iref2 != nullptr);
87  CheckDump(&irt, 3, 3);
88
89  EXPECT_OBJ_PTR_EQ(obj0.Get(), irt.Get(iref0));
90  EXPECT_OBJ_PTR_EQ(obj1.Get(), irt.Get(iref1));
91  EXPECT_OBJ_PTR_EQ(obj2.Get(), irt.Get(iref2));
92
93  EXPECT_TRUE(irt.Remove(cookie, iref0));
94  CheckDump(&irt, 2, 2);
95  EXPECT_TRUE(irt.Remove(cookie, iref1));
96  CheckDump(&irt, 1, 1);
97  EXPECT_TRUE(irt.Remove(cookie, iref2));
98  CheckDump(&irt, 0, 0);
99
100  // Table should be empty now.
101  EXPECT_EQ(0U, irt.Capacity());
102
103  // Get invalid entry (off the end of the list).
104  EXPECT_TRUE(irt.Get(iref0) == nullptr);
105
106  // Add three, remove in the opposite order.
107  iref0 = irt.Add(cookie, obj0.Get());
108  EXPECT_TRUE(iref0 != nullptr);
109  iref1 = irt.Add(cookie, obj1.Get());
110  EXPECT_TRUE(iref1 != nullptr);
111  iref2 = irt.Add(cookie, obj2.Get());
112  EXPECT_TRUE(iref2 != nullptr);
113  CheckDump(&irt, 3, 3);
114
115  ASSERT_TRUE(irt.Remove(cookie, iref2));
116  CheckDump(&irt, 2, 2);
117  ASSERT_TRUE(irt.Remove(cookie, iref1));
118  CheckDump(&irt, 1, 1);
119  ASSERT_TRUE(irt.Remove(cookie, iref0));
120  CheckDump(&irt, 0, 0);
121
122  // Table should be empty now.
123  ASSERT_EQ(0U, irt.Capacity());
124
125  // Add three, remove middle / middle / bottom / top.  (Second attempt
126  // to remove middle should fail.)
127  iref0 = irt.Add(cookie, obj0.Get());
128  EXPECT_TRUE(iref0 != nullptr);
129  iref1 = irt.Add(cookie, obj1.Get());
130  EXPECT_TRUE(iref1 != nullptr);
131  iref2 = irt.Add(cookie, obj2.Get());
132  EXPECT_TRUE(iref2 != nullptr);
133  CheckDump(&irt, 3, 3);
134
135  ASSERT_EQ(3U, irt.Capacity());
136
137  ASSERT_TRUE(irt.Remove(cookie, iref1));
138  CheckDump(&irt, 2, 2);
139  ASSERT_FALSE(irt.Remove(cookie, iref1));
140  CheckDump(&irt, 2, 2);
141
142  // Get invalid entry (from hole).
143  EXPECT_TRUE(irt.Get(iref1) == nullptr);
144
145  ASSERT_TRUE(irt.Remove(cookie, iref2));
146  CheckDump(&irt, 1, 1);
147  ASSERT_TRUE(irt.Remove(cookie, iref0));
148  CheckDump(&irt, 0, 0);
149
150  // Table should be empty now.
151  ASSERT_EQ(0U, irt.Capacity());
152
153  // Add four entries.  Remove #1, add new entry, verify that table size
154  // is still 4 (i.e. holes are getting filled).  Remove #1 and #3, verify
155  // that we delete one and don't hole-compact the other.
156  iref0 = irt.Add(cookie, obj0.Get());
157  EXPECT_TRUE(iref0 != nullptr);
158  iref1 = irt.Add(cookie, obj1.Get());
159  EXPECT_TRUE(iref1 != nullptr);
160  iref2 = irt.Add(cookie, obj2.Get());
161  EXPECT_TRUE(iref2 != nullptr);
162  IndirectRef iref3 = irt.Add(cookie, obj3.Get());
163  EXPECT_TRUE(iref3 != nullptr);
164  CheckDump(&irt, 4, 4);
165
166  ASSERT_TRUE(irt.Remove(cookie, iref1));
167  CheckDump(&irt, 3, 3);
168
169  iref1 = irt.Add(cookie, obj1.Get());
170  EXPECT_TRUE(iref1 != nullptr);
171
172  ASSERT_EQ(4U, irt.Capacity()) << "hole not filled";
173  CheckDump(&irt, 4, 4);
174
175  ASSERT_TRUE(irt.Remove(cookie, iref1));
176  CheckDump(&irt, 3, 3);
177  ASSERT_TRUE(irt.Remove(cookie, iref3));
178  CheckDump(&irt, 2, 2);
179
180  ASSERT_EQ(3U, irt.Capacity()) << "should be 3 after two deletions";
181
182  ASSERT_TRUE(irt.Remove(cookie, iref2));
183  CheckDump(&irt, 1, 1);
184  ASSERT_TRUE(irt.Remove(cookie, iref0));
185  CheckDump(&irt, 0, 0);
186
187  ASSERT_EQ(0U, irt.Capacity()) << "not empty after split remove";
188
189  // Add an entry, remove it, add a new entry, and try to use the original
190  // iref.  They have the same slot number but are for different objects.
191  // With the extended checks in place, this should fail.
192  iref0 = irt.Add(cookie, obj0.Get());
193  EXPECT_TRUE(iref0 != nullptr);
194  CheckDump(&irt, 1, 1);
195  ASSERT_TRUE(irt.Remove(cookie, iref0));
196  CheckDump(&irt, 0, 0);
197  iref1 = irt.Add(cookie, obj1.Get());
198  EXPECT_TRUE(iref1 != nullptr);
199  CheckDump(&irt, 1, 1);
200  ASSERT_FALSE(irt.Remove(cookie, iref0)) << "mismatched del succeeded";
201  CheckDump(&irt, 1, 1);
202  ASSERT_TRUE(irt.Remove(cookie, iref1)) << "switched del failed";
203  ASSERT_EQ(0U, irt.Capacity()) << "switching del not empty";
204  CheckDump(&irt, 0, 0);
205
206  // Same as above, but with the same object.  A more rigorous checker
207  // (e.g. with slot serialization) will catch this.
208  iref0 = irt.Add(cookie, obj0.Get());
209  EXPECT_TRUE(iref0 != nullptr);
210  CheckDump(&irt, 1, 1);
211  ASSERT_TRUE(irt.Remove(cookie, iref0));
212  CheckDump(&irt, 0, 0);
213  iref1 = irt.Add(cookie, obj0.Get());
214  EXPECT_TRUE(iref1 != nullptr);
215  CheckDump(&irt, 1, 1);
216  if (iref0 != iref1) {
217    // Try 0, should not work.
218    ASSERT_FALSE(irt.Remove(cookie, iref0)) << "temporal del succeeded";
219  }
220  ASSERT_TRUE(irt.Remove(cookie, iref1)) << "temporal cleanup failed";
221  ASSERT_EQ(0U, irt.Capacity()) << "temporal del not empty";
222  CheckDump(&irt, 0, 0);
223
224  // null isn't a valid iref.
225  ASSERT_TRUE(irt.Get(nullptr) == nullptr);
226
227  // Stale lookup.
228  iref0 = irt.Add(cookie, obj0.Get());
229  EXPECT_TRUE(iref0 != nullptr);
230  CheckDump(&irt, 1, 1);
231  ASSERT_TRUE(irt.Remove(cookie, iref0));
232  EXPECT_TRUE(irt.Get(iref0) == nullptr) << "stale lookup succeeded";
233  CheckDump(&irt, 0, 0);
234
235  // Test table resizing.
236  // These ones fit...
237  static const size_t kTableInitial = kTableMax / 2;
238  IndirectRef manyRefs[kTableInitial];
239  for (size_t i = 0; i < kTableInitial; i++) {
240    manyRefs[i] = irt.Add(cookie, obj0.Get());
241    ASSERT_TRUE(manyRefs[i] != nullptr) << "Failed adding " << i;
242    CheckDump(&irt, i + 1, 1);
243  }
244  // ...this one causes overflow.
245  iref0 = irt.Add(cookie, obj0.Get());
246  ASSERT_TRUE(iref0 != nullptr);
247  ASSERT_EQ(kTableInitial + 1, irt.Capacity());
248  CheckDump(&irt, kTableInitial + 1, 1);
249
250  for (size_t i = 0; i < kTableInitial; i++) {
251    ASSERT_TRUE(irt.Remove(cookie, manyRefs[i])) << "failed removing " << i;
252    CheckDump(&irt, kTableInitial - i, 1);
253  }
254  // Because of removal order, should have 11 entries, 10 of them holes.
255  ASSERT_EQ(kTableInitial + 1, irt.Capacity());
256
257  ASSERT_TRUE(irt.Remove(cookie, iref0)) << "multi-remove final failed";
258
259  ASSERT_EQ(0U, irt.Capacity()) << "multi-del not empty";
260  CheckDump(&irt, 0, 0);
261}
262
263TEST_F(IndirectReferenceTableTest, Holes) {
264  // Test the explicitly named cases from the IRT implementation:
265  //
266  // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference
267  // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
268  // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
269  //    reference
270  // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference
271  // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
272  //    reference
273
274  ScopedObjectAccess soa(Thread::Current());
275  static const size_t kTableMax = 10;
276
277  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
278  StackHandleScope<5> hs(soa.Self());
279  ASSERT_TRUE(c != nullptr);
280  Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
281  ASSERT_TRUE(obj0.Get() != nullptr);
282  Handle<mirror::Object> obj1 = hs.NewHandle(c->AllocObject(soa.Self()));
283  ASSERT_TRUE(obj1.Get() != nullptr);
284  Handle<mirror::Object> obj2 = hs.NewHandle(c->AllocObject(soa.Self()));
285  ASSERT_TRUE(obj2.Get() != nullptr);
286  Handle<mirror::Object> obj3 = hs.NewHandle(c->AllocObject(soa.Self()));
287  ASSERT_TRUE(obj3.Get() != nullptr);
288  Handle<mirror::Object> obj4 = hs.NewHandle(c->AllocObject(soa.Self()));
289  ASSERT_TRUE(obj4.Get() != nullptr);
290
291  std::string error_msg;
292
293  // 1) Segment with holes (current_num_holes_ > 0), push new segment, add/remove reference.
294  {
295    IndirectReferenceTable irt(kTableMax,
296                               kGlobal,
297                               IndirectReferenceTable::ResizableCapacity::kNo,
298                               &error_msg);
299    ASSERT_TRUE(irt.IsValid()) << error_msg;
300
301    const IRTSegmentState cookie0 = kIRTFirstSegment;
302
303    CheckDump(&irt, 0, 0);
304
305    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
306    IndirectRef iref1 = irt.Add(cookie0, obj1.Get());
307    IndirectRef iref2 = irt.Add(cookie0, obj2.Get());
308
309    EXPECT_TRUE(irt.Remove(cookie0, iref1));
310
311    // New segment.
312    const IRTSegmentState cookie1 = irt.GetSegmentState();
313
314    IndirectRef iref3 = irt.Add(cookie1, obj3.Get());
315
316    // Must not have filled the previous hole.
317    EXPECT_EQ(irt.Capacity(), 4u);
318    EXPECT_TRUE(irt.Get(iref1) == nullptr);
319    CheckDump(&irt, 3, 3);
320
321    UNUSED(iref0, iref1, iref2, iref3);
322  }
323
324  // 2) Segment with holes (current_num_holes_ > 0), pop segment, add/remove reference
325  {
326    IndirectReferenceTable irt(kTableMax,
327                               kGlobal,
328                               IndirectReferenceTable::ResizableCapacity::kNo,
329                               &error_msg);
330    ASSERT_TRUE(irt.IsValid()) << error_msg;
331
332    const IRTSegmentState cookie0 = kIRTFirstSegment;
333
334    CheckDump(&irt, 0, 0);
335
336    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
337
338    // New segment.
339    const IRTSegmentState cookie1 = irt.GetSegmentState();
340
341    IndirectRef iref1 = irt.Add(cookie1, obj1.Get());
342    IndirectRef iref2 = irt.Add(cookie1, obj2.Get());
343    IndirectRef iref3 = irt.Add(cookie1, obj3.Get());
344
345    EXPECT_TRUE(irt.Remove(cookie1, iref2));
346
347    // Pop segment.
348    irt.SetSegmentState(cookie1);
349
350    IndirectRef iref4 = irt.Add(cookie1, obj4.Get());
351
352    EXPECT_EQ(irt.Capacity(), 2u);
353    EXPECT_TRUE(irt.Get(iref2) == nullptr);
354    CheckDump(&irt, 2, 2);
355
356    UNUSED(iref0, iref1, iref2, iref3, iref4);
357  }
358
359  // 3) Segment with holes (current_num_holes_ > 0), push new segment, pop segment, add/remove
360  //    reference.
361  {
362    IndirectReferenceTable irt(kTableMax,
363                               kGlobal,
364                               IndirectReferenceTable::ResizableCapacity::kNo,
365                               &error_msg);
366    ASSERT_TRUE(irt.IsValid()) << error_msg;
367
368    const IRTSegmentState cookie0 = kIRTFirstSegment;
369
370    CheckDump(&irt, 0, 0);
371
372    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
373
374    // New segment.
375    const IRTSegmentState cookie1 = irt.GetSegmentState();
376
377    IndirectRef iref1 = irt.Add(cookie1, obj1.Get());
378    IndirectRef iref2 = irt.Add(cookie1, obj2.Get());
379
380    EXPECT_TRUE(irt.Remove(cookie1, iref1));
381
382    // New segment.
383    const IRTSegmentState cookie2 = irt.GetSegmentState();
384
385    IndirectRef iref3 = irt.Add(cookie2, obj3.Get());
386
387    // Pop segment.
388    irt.SetSegmentState(cookie2);
389
390    IndirectRef iref4 = irt.Add(cookie1, obj4.Get());
391
392    EXPECT_EQ(irt.Capacity(), 3u);
393    EXPECT_TRUE(irt.Get(iref1) == nullptr);
394    CheckDump(&irt, 3, 3);
395
396    UNUSED(iref0, iref1, iref2, iref3, iref4);
397  }
398
399  // 4) Empty segment, push new segment, create a hole, pop a segment, add/remove a reference.
400  {
401    IndirectReferenceTable irt(kTableMax,
402                               kGlobal,
403                               IndirectReferenceTable::ResizableCapacity::kNo,
404                               &error_msg);
405    ASSERT_TRUE(irt.IsValid()) << error_msg;
406
407    const IRTSegmentState cookie0 = kIRTFirstSegment;
408
409    CheckDump(&irt, 0, 0);
410
411    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
412
413    // New segment.
414    const IRTSegmentState cookie1 = irt.GetSegmentState();
415
416    IndirectRef iref1 = irt.Add(cookie1, obj1.Get());
417    EXPECT_TRUE(irt.Remove(cookie1, iref1));
418
419    // Emptied segment, push new one.
420    const IRTSegmentState cookie2 = irt.GetSegmentState();
421
422    IndirectRef iref2 = irt.Add(cookie1, obj1.Get());
423    IndirectRef iref3 = irt.Add(cookie1, obj2.Get());
424    IndirectRef iref4 = irt.Add(cookie1, obj3.Get());
425
426    EXPECT_TRUE(irt.Remove(cookie1, iref3));
427
428    // Pop segment.
429    UNUSED(cookie2);
430    irt.SetSegmentState(cookie1);
431
432    IndirectRef iref5 = irt.Add(cookie1, obj4.Get());
433
434    EXPECT_EQ(irt.Capacity(), 2u);
435    EXPECT_TRUE(irt.Get(iref3) == nullptr);
436    CheckDump(&irt, 2, 2);
437
438    UNUSED(iref0, iref1, iref2, iref3, iref4, iref5);
439  }
440
441  // 5) Base segment, push new segment, create a hole, pop a segment, push new segment, add/remove
442  //    reference
443  {
444    IndirectReferenceTable irt(kTableMax,
445                               kGlobal,
446                               IndirectReferenceTable::ResizableCapacity::kNo,
447                               &error_msg);
448    ASSERT_TRUE(irt.IsValid()) << error_msg;
449
450    const IRTSegmentState cookie0 = kIRTFirstSegment;
451
452    CheckDump(&irt, 0, 0);
453
454    IndirectRef iref0 = irt.Add(cookie0, obj0.Get());
455
456    // New segment.
457    const IRTSegmentState cookie1 = irt.GetSegmentState();
458
459    IndirectRef iref1 = irt.Add(cookie1, obj1.Get());
460    IndirectRef iref2 = irt.Add(cookie1, obj1.Get());
461    IndirectRef iref3 = irt.Add(cookie1, obj2.Get());
462
463    EXPECT_TRUE(irt.Remove(cookie1, iref2));
464
465    // Pop segment.
466    irt.SetSegmentState(cookie1);
467
468    // Push segment.
469    const IRTSegmentState cookie1_second = irt.GetSegmentState();
470    UNUSED(cookie1_second);
471
472    IndirectRef iref4 = irt.Add(cookie1, obj3.Get());
473
474    EXPECT_EQ(irt.Capacity(), 2u);
475    EXPECT_TRUE(irt.Get(iref3) == nullptr);
476    CheckDump(&irt, 2, 2);
477
478    UNUSED(iref0, iref1, iref2, iref3, iref4);
479  }
480}
481
482TEST_F(IndirectReferenceTableTest, Resize) {
483  ScopedObjectAccess soa(Thread::Current());
484  static const size_t kTableMax = 512;
485
486  mirror::Class* c = class_linker_->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
487  StackHandleScope<1> hs(soa.Self());
488  ASSERT_TRUE(c != nullptr);
489  Handle<mirror::Object> obj0 = hs.NewHandle(c->AllocObject(soa.Self()));
490  ASSERT_TRUE(obj0.Get() != nullptr);
491
492  std::string error_msg;
493  IndirectReferenceTable irt(kTableMax,
494                             kLocal,
495                             IndirectReferenceTable::ResizableCapacity::kYes,
496                             &error_msg);
497  ASSERT_TRUE(irt.IsValid()) << error_msg;
498
499  CheckDump(&irt, 0, 0);
500  const IRTSegmentState cookie = kIRTFirstSegment;
501
502  for (size_t i = 0; i != kTableMax + 1; ++i) {
503    irt.Add(cookie, obj0.Get());
504  }
505
506  EXPECT_EQ(irt.Capacity(), kTableMax + 1);
507}
508
509}  // namespace art
510