BpfMapTest: add fd leak test
Test that there is no fd leak with persistent fd cache.
Bug: 236320567
Test: atest BpfMapTest#testNoFdLeaks (cached fd) [PASSED]
Test: atest BpfMapTest#testNoFdLeaks (noncached fd) [FAILED] (as expected)
[8/14] com.android.networkstack.tethering.BpfMapTest#testNoFdLeaks: FAILED (241ms)
STACKTRACE:
java.lang.AssertionError: Fd leak after 1000 iterations: expected:<89> but was:<1089>
at org.junit.Assert.fail(Assert.java:89)
at org.junit.Assert.failNotEquals(Assert.java:835)
at org.junit.Assert.assertEquals(Assert.java:647)
at com.android.networkstack.tethering.BpfMapTest.testNoFdLeaks(BpfMapTest.java:420)
Test Code:
fd noncached BpfMap
frameworks/libs/net/common/device/com/android/net/module/util/BpfMap.java
@@ -97,7 +97,7 @@ public class BpfMap<K extends Struct, V extends Struct> implements IBpfMap<K, V>
*/
public BpfMap(@NonNull final String path, final int flag, final Class<K> key,
final Class<V> value) throws ErrnoException, NullPointerException {
- mMapFd = cachedBpfFdGet(path, flag);
+ mMapFd = ParcelFileDescriptor.adoptFd(nativeBpfFdGet(path, flag));
Change-Id: I66f477fd1c291c56bccc97d385b2a554c2367b5a
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
index 68c1c57..536ab2d 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
@@ -30,6 +30,7 @@
import android.net.MacAddress;
import android.os.Build;
import android.system.ErrnoException;
+import android.system.Os;
import android.system.OsConstants;
import android.util.ArrayMap;
@@ -42,6 +43,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.File;
import java.net.InetAddress;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicInteger;
@@ -393,4 +395,34 @@
assertEquals(OsConstants.ENOENT, expected.errno);
}
}
+
+ private static int getNumOpenFds() {
+ return new File("/proc/" + Os.getpid() + "/fd").listFiles().length;
+ }
+
+ @Test
+ public void testNoFdLeaks() throws Exception {
+ // Due to #setUp has called #initTestMap to open map and BpfMap is using persistent fd
+ // cache, expect that the fd amount is not increased in the iterations.
+ // See the comment of BpfMap#close.
+ final int iterations = 1000;
+ final int before = getNumOpenFds();
+ for (int i = 0; i < iterations; i++) {
+ try (BpfMap<TetherDownstream6Key, Tether6Value> map = new BpfMap<>(
+ TETHER_DOWNSTREAM6_FS_PATH, BpfMap.BPF_F_RDWR,
+ TetherDownstream6Key.class, Tether6Value.class)) {
+ // do nothing
+ }
+ }
+ final int after = getNumOpenFds();
+
+ // Check that the number of open fds is the same as before.
+ // If this exact match becomes flaky, we probably need to distinguish that fd is belong
+ // to "bpf-map".
+ // ex:
+ // $ adb shell ls -all /proc/16196/fd
+ // [..] network_stack 64 2022-07-26 22:01:02.300002956 +0800 749 -> anon_inode:bpf-map
+ // [..] network_stack 64 2022-07-26 22:01:02.188002956 +0800 75 -> anon_inode:[eventfd]
+ assertEquals("Fd leak after " + iterations + " iterations: ", before, after);
+ }
}