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