blob: e9f6450c13aabe0b63b4a3147728f326227db68e [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import android.content.Context;
import android.os.FileUtils;
import android.util.IntArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.File;
import java.nio.file.Files;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BinderfsStatsReaderTest {
private static final String BINDER_LOGS_STATS_HEADER = """
binder stats:
BC_TRANSACTION: 695756
BC_REPLY: 547779
BC_FREE_BUFFER: 1283223
BR_FAILED_REPLY: 4
BR_FROZEN_REPLY: 3
BR_ONEWAY_SPAM_SUSPECT: 1
proc: active 313 total 377
thread: active 3077 total 5227
""";
private static final String BINDER_LOGS_STATS_PROC1 = """
proc 14505
context binder
threads: 4
requested threads: 0+2/15
ready threads 0
free async space 520192
nodes: 9
refs: 29 s 29 w 29
buffers: 0
""";
private static final String BINDER_LOGS_STATS_PROC2 = """
proc 14461
context binder
threads: 8
requested threads: 0+2/15
ready threads 0
free async space 62
nodes: 30
refs: 51 s 51 w 51
buffers: 0
""";
private static final String BINDER_LOGS_STATS_PROC3 = """
proc 542
context binder
threads: 2
requested threads: 0+0/15
ready threads 0
free async space 519896
nodes: 1
refs: 2 s 3 w 2
buffers: 1
""";
private static final String BINDER_LOGS_STATS_PROC4 = """
proc 540
context binder
threads: 1
requested threads: 0+0/0
ready threads 1
free async space 44
nodes: 4
refs: 1 s 1 w 1
buffers: 0
""";
private File mStatsDirectory;
private int mFreezerBinderAsyncThreshold;
private IntArray mValidPids; // The pool of valid pids
private IntArray mStatsPids; // The pids read from binderfs stats that are also valid
private IntArray mStatsFree; // The free async space of the above pids
private boolean mHasError;
@Before
public void setUp() {
Context context = InstrumentationRegistry.getContext();
mStatsDirectory = context.getDir("binder_logs", Context.MODE_PRIVATE);
mFreezerBinderAsyncThreshold = 1024;
mValidPids = IntArray.fromArray(new int[]{14505, 14461, 542, 540}, 4);
mStatsPids = new IntArray();
mStatsFree = new IntArray();
mHasError = false;
}
@After
public void tearDown() throws Exception {
FileUtils.deleteContents(mStatsDirectory);
}
@Test
public void testNoneProc() throws Exception {
runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER);
assertFalse(mHasError);
assertEquals(0, mStatsPids.size());
assertEquals(0, mStatsFree.size());
}
@Test
public void testOneProc() throws Exception {
runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1);
assertFalse(mHasError);
assertEquals(0, mStatsPids.size());
assertEquals(0, mStatsFree.size());
}
@Test
public void testTwoProc() throws Exception {
runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1
+ BINDER_LOGS_STATS_PROC2);
assertFalse(mHasError);
assertArrayEquals(mStatsPids.toArray(), new int[]{14461});
assertArrayEquals(mStatsFree.toArray(), new int[]{62});
}
@Test
public void testThreeProc() throws Exception {
runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1
+ BINDER_LOGS_STATS_PROC2 + BINDER_LOGS_STATS_PROC3);
assertFalse(mHasError);
assertArrayEquals(mStatsPids.toArray(), new int[]{14461});
assertArrayEquals(mStatsFree.toArray(), new int[]{62});
}
@Test
public void testFourProc() throws Exception {
runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1
+ BINDER_LOGS_STATS_PROC2 + BINDER_LOGS_STATS_PROC3 + BINDER_LOGS_STATS_PROC4);
assertFalse(mHasError);
assertArrayEquals(mStatsPids.toArray(), new int[]{14461, 540});
assertArrayEquals(mStatsFree.toArray(), new int[]{62, 44});
}
@Test
public void testInvalidProc() throws Exception {
mValidPids = new IntArray();
runHandleBlockingFileLocks(BINDER_LOGS_STATS_HEADER + BINDER_LOGS_STATS_PROC1
+ BINDER_LOGS_STATS_PROC2 + BINDER_LOGS_STATS_PROC3 + BINDER_LOGS_STATS_PROC4);
assertFalse(mHasError);
assertEquals(0, mStatsPids.size());
assertEquals(0, mStatsFree.size());
}
private void runHandleBlockingFileLocks(String fileContents) throws Exception {
File tempFile = File.createTempFile("stats", null, mStatsDirectory);
Files.write(tempFile.toPath(), fileContents.getBytes());
new BinderfsStatsReader(tempFile.toString()).handleFreeAsyncSpace(
// Check if the current process is a valid one
mValidPids::contains,
// Check if the current process is running out of async binder space
(pid, free) -> {
if (free < mFreezerBinderAsyncThreshold) {
mStatsPids.add(pid);
mStatsFree.add(free);
}
},
// Log the error if binderfs stats can't be accesses or correctly parsed
exception -> {
mHasError = true;
});
Files.delete(tempFile.toPath());
}
}