TestIndirectRefTable.cpp revision 476157d87784f48935cb86f01c079db1f9338e36
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/*
18 * Test the indirect reference table implementation.
19 */
20#include "Dalvik.h"
21
22#include <stdlib.h>
23#include <sys/time.h>
24
25#ifndef NDEBUG
26
27#define DBUG_MSG    LOGI
28
29class Stopwatch {
30public:
31    Stopwatch() {
32        reset();
33    }
34
35    void reset() {
36        start_ = now();
37    }
38
39    float elapsedSeconds() {
40        return (now() - start_) * 0.000001f;
41    }
42
43private:
44    u8 start_;
45
46    static u8 now() {
47#ifdef HAVE_POSIX_CLOCKS
48        struct timespec tm;
49        clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
50        return tm.tv_sec * 1000000LL + tm.tv_nsec / 1000;
51#else
52        struct timeval tv;
53        gettimeofday(&tv, NULL);
54        return tv.tv_sec * 1000000LL + tv.tv_usec;
55#endif
56    }
57};
58
59/*
60 * Basic add/get/delete tests in an unsegmented table.
61 */
62static bool basicTest()
63{
64    static const int kTableMax = 20;
65    IndirectRefTable irt;
66    IndirectRef iref0, iref1, iref2, iref3;
67    IndirectRef manyRefs[kTableMax];
68    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
69    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
70    Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
71    Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
72    Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
73    const u4 cookie = IRT_FIRST_SEGMENT;
74    bool result = false;
75
76    if (!irt.init(kTableMax/2, kTableMax, kIndirectKindGlobal)) {
77        return false;
78    }
79
80    iref0 = (IndirectRef) 0x11110;
81    if (irt.remove(cookie, iref0)) {
82        LOGE("unexpectedly successful removal");
83        goto bail;
84    }
85
86    /*
87     * Add three, check, remove in the order in which they were added.
88     */
89    DBUG_MSG("+++ START fifo\n");
90    iref0 = irt.add(cookie, obj0);
91    iref1 = irt.add(cookie, obj1);
92    iref2 = irt.add(cookie, obj2);
93    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
94        LOGE("trivial add1 failed");
95        goto bail;
96    }
97
98    if (irt.get(iref0) != obj0 ||
99            irt.get(iref1) != obj1 ||
100            irt.get(iref2) != obj2) {
101        LOGE("objects don't match expected values %p %p %p vs. %p %p %p",
102                irt.get(iref0), irt.get(iref1), irt.get(iref2),
103                obj0, obj1, obj2);
104        goto bail;
105    } else {
106        DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1);
107    }
108
109    if (!irt.remove(cookie, iref0) ||
110            !irt.remove(cookie, iref1) ||
111            !irt.remove(cookie, iref2))
112    {
113        LOGE("fifo deletion failed");
114        goto bail;
115    }
116
117    /* table should be empty now */
118    if (irt.capacity() != 0) {
119        LOGE("fifo del not empty");
120        goto bail;
121    }
122
123    /* get invalid entry (off the end of the list) */
124    if (irt.get(iref0) != kInvalidIndirectRefObject) {
125        LOGE("stale entry get succeeded unexpectedly");
126        goto bail;
127    }
128
129    /*
130     * Add three, remove in the opposite order.
131     */
132    DBUG_MSG("+++ START lifo\n");
133    iref0 = irt.add(cookie, obj0);
134    iref1 = irt.add(cookie, obj1);
135    iref2 = irt.add(cookie, obj2);
136    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
137        LOGE("trivial add2 failed");
138        goto bail;
139    }
140
141    if (!irt.remove(cookie, iref2) ||
142            !irt.remove(cookie, iref1) ||
143            !irt.remove(cookie, iref0))
144    {
145        LOGE("lifo deletion failed");
146        goto bail;
147    }
148
149    /* table should be empty now */
150    if (irt.capacity() != 0) {
151        LOGE("lifo del not empty");
152        goto bail;
153    }
154
155    /*
156     * Add three, remove middle / middle / bottom / top.  (Second attempt
157     * to remove middle should fail.)
158     */
159    DBUG_MSG("+++ START unorder\n");
160    iref0 = irt.add(cookie, obj0);
161    iref1 = irt.add(cookie, obj1);
162    iref2 = irt.add(cookie, obj2);
163    if (iref0 == NULL || iref1 == NULL || iref2 == NULL) {
164        LOGE("trivial add3 failed");
165        goto bail;
166    }
167
168    if (irt.capacity() != 3) {
169        LOGE("expected 3 entries, found %d", irt.capacity());
170        goto bail;
171    }
172
173    if (!irt.remove(cookie, iref1) || irt.remove(cookie, iref1)) {
174        LOGE("unorder deletion1 failed");
175        goto bail;
176    }
177
178    /* get invalid entry (from hole) */
179    if (irt.get(iref1) != kInvalidIndirectRefObject) {
180        LOGE("hole get succeeded unexpectedly");
181        goto bail;
182    }
183
184    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
185        LOGE("unorder deletion2 failed");
186        goto bail;
187    }
188
189    /* table should be empty now */
190    if (irt.capacity() != 0) {
191        LOGE("unorder del not empty");
192        goto bail;
193    }
194
195    /*
196     * Add four entries.  Remove #1, add new entry, verify that table size
197     * is still 4 (i.e. holes are getting filled).  Remove #1 and #3, verify
198     * that we delete one and don't hole-compact the other.
199     */
200    DBUG_MSG("+++ START hole fill\n");
201    iref0 = irt.add(cookie, obj0);
202    iref1 = irt.add(cookie, obj1);
203    iref2 = irt.add(cookie, obj2);
204    iref3 = irt.add(cookie, obj3);
205    if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) {
206        LOGE("trivial add4 failed");
207        goto bail;
208    }
209    if (!irt.remove(cookie, iref1)) {
210        LOGE("remove 1 of 4 failed");
211        goto bail;
212    }
213    iref1 = irt.add(cookie, obj1);
214    if (irt.capacity() != 4) {
215        LOGE("hole not filled");
216        goto bail;
217    }
218    if (!irt.remove(cookie, iref1) || !irt.remove(cookie, iref3)) {
219        LOGE("remove 1/3 failed");
220        goto bail;
221    }
222    if (irt.capacity() != 3) {
223        LOGE("should be 3 after two deletions");
224        goto bail;
225    }
226    if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) {
227        LOGE("remove 2/0 failed");
228        goto bail;
229    }
230    if (irt.capacity() != 0) {
231        LOGE("not empty after split remove");
232        goto bail;
233    }
234
235    /*
236     * Add an entry, remove it, add a new entry, and try to use the original
237     * iref.  They have the same slot number but are for different objects.
238     * With the extended checks in place, this should fail.
239     */
240    DBUG_MSG("+++ START switched\n");
241    iref0 = irt.add(cookie, obj0);
242    irt.remove(cookie, iref0);
243    iref1 = irt.add(cookie, obj1);
244    if (irt.remove(cookie, iref0)) {
245        LOGE("mismatched del succeeded (%p vs %p)", iref0, iref1);
246        goto bail;
247    }
248    if (!irt.remove(cookie, iref1)) {
249        LOGE("switched del failed");
250        goto bail;
251    }
252    if (irt.capacity() != 0) {
253        LOGE("switching del not empty");
254        goto bail;
255    }
256
257    /*
258     * Same as above, but with the same object.  A more rigorous checker
259     * (e.g. with slot serialization) will catch this.
260     */
261    DBUG_MSG("+++ START switched same object\n");
262    iref0 = irt.add(cookie, obj0);
263    irt.remove(cookie, iref0);
264    iref1 = irt.add(cookie, obj0);
265    if (iref0 != iref1) {
266        /* try 0, should not work */
267        if (irt.remove(cookie, iref0)) {
268            LOGE("temporal del succeeded (%p vs %p)", iref0, iref1);
269            goto bail;
270        }
271    }
272    if (!irt.remove(cookie, iref1)) {
273        LOGE("temporal cleanup failed");
274        goto bail;
275    }
276    if (irt.capacity() != 0) {
277        LOGE("temporal del not empty");
278        goto bail;
279    }
280
281    DBUG_MSG("+++ START null lookup\n");
282    if (irt.get(NULL) != kInvalidIndirectRefObject) {
283        LOGE("null lookup succeeded");
284        goto bail;
285    }
286
287    DBUG_MSG("+++ START stale lookup\n");
288    iref0 = irt.add(cookie, obj0);
289    irt.remove(cookie, iref0);
290    if (irt.get(iref0) != kInvalidIndirectRefObject) {
291        LOGE("stale lookup succeeded");
292        goto bail;
293    }
294
295    /*
296     * Test table overflow.
297     */
298    DBUG_MSG("+++ START overflow\n");
299    int i;
300    for (i = 0; i < kTableMax; i++) {
301        manyRefs[i] = irt.add(cookie, obj0);
302        if (manyRefs[i] == NULL) {
303            LOGE("Failed adding %d of %d", i, kTableMax);
304            goto bail;
305        }
306    }
307    if (irt.add(cookie, obj0) != NULL) {
308        LOGE("Table overflow succeeded");
309        goto bail;
310    }
311    if (irt.capacity() != (size_t)kTableMax) {
312        LOGE("Expected %d entries, found %d", kTableMax, irt.capacity());
313        goto bail;
314    }
315    for (i = 0; i < kTableMax-1; i++) {
316        if (!irt.remove(cookie, manyRefs[i])) {
317            LOGE("multi-remove failed at %d", i);
318            goto bail;
319        }
320    }
321    /* because of removal order, should have 20 entries, 19 of them holes */
322    if (irt.capacity() != (size_t)kTableMax) {
323        LOGE("Expected %d entries (with holes), found %d",
324                kTableMax, irt.capacity());
325        goto bail;
326    }
327    if (!irt.remove(cookie, manyRefs[kTableMax-1])) {
328        LOGE("multi-remove final failed");
329        goto bail;
330    }
331    if (irt.capacity() != 0) {
332        LOGE("multi-del not empty");
333        goto bail;
334    }
335
336    DBUG_MSG("+++ basic test complete\n");
337    result = true;
338
339bail:
340    irt.destroy();
341    return result;
342}
343
344static bool performanceTest()
345{
346    static const int kTableMax = 100;
347    IndirectRefTable irt;
348    IndirectRef manyRefs[kTableMax];
349    ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL);
350    Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
351    const u4 cookie = IRT_FIRST_SEGMENT;
352    const int kLoops = 100000;
353    Stopwatch stopwatch;
354
355    DBUG_MSG("+++ START performance\n");
356
357    if (!irt.init(kTableMax, kTableMax, kIndirectKindGlobal)) {
358        return false;
359    }
360
361    stopwatch.reset();
362    for (int loop = 0; loop < kLoops; loop++) {
363        for (int i = 0; i < kTableMax; i++) {
364            manyRefs[i] = irt.add(cookie, obj0);
365        }
366        for (int i = 0; i < kTableMax; i++) {
367            irt.remove(cookie, manyRefs[i]);
368        }
369    }
370    DBUG_MSG("Add/remove %d objects FIFO order, %d iterations, %0.3fms / iteration",
371            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000 / kLoops);
372
373    stopwatch.reset();
374    for (int loop = 0; loop < kLoops; loop++) {
375        for (int i = 0; i < kTableMax; i++) {
376            manyRefs[i] = irt.add(cookie, obj0);
377        }
378        for (int i = kTableMax; i-- > 0; ) {
379            irt.remove(cookie, manyRefs[i]);
380        }
381    }
382    DBUG_MSG("Add/remove %d objects LIFO order, %d iterations, %0.3fms / iteration",
383            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000  / kLoops);
384
385    for (int i = 0; i < kTableMax; i++) {
386        manyRefs[i] = irt.add(cookie, obj0);
387    }
388    stopwatch.reset();
389    for (int loop = 0; loop < kLoops; loop++) {
390        for (int i = 0; i < kTableMax; i++) {
391            irt.get(manyRefs[i]);
392        }
393    }
394    DBUG_MSG("Get %d objects, %d iterations, %0.3fms / iteration",
395            kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000  / kLoops);
396    for (int i = kTableMax; i-- > 0; ) {
397        irt.remove(cookie, manyRefs[i]);
398    }
399
400    irt.destroy();
401    return true;
402}
403
404/*
405 * Some quick tests.
406 */
407bool dvmTestIndirectRefTable()
408{
409    if (!basicTest()) {
410        LOGE("IRT basic test failed");
411        return false;
412    }
413
414    if (!performanceTest()) {
415        LOGE("IRT performance test failed");
416        return false;
417    }
418
419    return true;
420}
421
422#endif /*NDEBUG*/
423