1 /*
2  * Copyright 2024 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 package android.view.cts.util
17 
18 import android.os.Debug
19 import java.util.concurrent.CountDownLatch
20 import java.util.concurrent.TimeUnit
21 import org.junit.Assert
22 
23 /**
24  * Helper class to detect native heap memory leaks in tests.
25  * Example usage:
26  *     ... Allocate any memory required for test
27  *     try (NativeHeapLeakDetector d = new NativeHeapLeakDetector()) {
28  *         ... code to be tested for memory leak ...
29  *     }
30  * Note: This only reports leaks larger then MEMORY_LEAK_THRESHOLD_KB (35 KB). Its recommended to
31  * run the code to be tested for memory leak sufficient times for the leak to large enough to be
32  * detected.
33  */
34 class NativeHeapLeakDetector : AutoCloseable {
35     private val mInitialNativeHeapUsage: Long
36 
37     init {
38         runGcAndFinalizersSync()
39         mInitialNativeHeapUsage = Debug.getNativeHeapAllocatedSize()
40     }
41 
42     /**
43      * MemoryInfo values are not always precise and reliable, to prevent the test from flake we are
44      * using a arbitrary limit of MEMORY_LEAK_THRESHOLD_KB (35 KB) over multiple iterations. It
45      * should still allow us to catch major memory leaks avoiding flakiness in the test.
46      */
closenull47     override fun close() {
48         runGcAndFinalizersSync()
49         val leakedNativeHeapKB =
50                 (Debug.getNativeHeapAllocatedSize() - mInitialNativeHeapUsage) / 1024
51         Assert.assertFalse(
52                 "Possible Memory leak. Leaked native memory usage: $leakedNativeHeapKB KB",
53                 leakedNativeHeapKB > MEMORY_LEAK_THRESHOLD_KB // KB
54         )
55     }
56 
57     companion object {
58         const val MEMORY_LEAK_THRESHOLD_KB = 35
runGcAndFinalizersSyncnull59         private fun runGcAndFinalizersSync() {
60             // This is a simple way to wait for all finalizers to run.
61             // It is not guaranteed to work, but it is sufficient for our purposes.
62             val fence = CountDownLatch(1)
63             object : Any() {
64                 protected fun finalize() {
65                     fence.countDown()
66                 }
67             }
68             try {
69                 do {
70                     Runtime.getRuntime().gc()
71                     Runtime.getRuntime().runFinalization()
72                 } while (!fence.await(100, TimeUnit.MILLISECONDS))
73             } catch (ex: InterruptedException) {
74                 throw RuntimeException(ex)
75             }
76         }
77     }
78 }
79