/* * 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 com.android.internal.util.ProcFileReader; import java.io.FileInputStream; import java.io.IOException; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; /** * Reads and parses {@code binder_logs/stats} file in the {@code binderfs} filesystem. * Reuse procFileReader as the contents are generated by Linux kernel in the same way. * * A typical example of binderfs stats log * * binder stats: * BC_TRANSACTION: 378004 * BC_REPLY: 268352 * BC_FREE_BUFFER: 665854 * ... * proc 12645 * context binder * threads: 12 * requested threads: 0+5/15 * ready threads 0 * free async space 520192 * ... */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public class BinderfsStatsReader { private final String mPath; public BinderfsStatsReader() { mPath = "/dev/binderfs/binder_logs/stats"; } public BinderfsStatsReader(String path) { mPath = path; } /** * Read binderfs stats and call the consumer(pid, free) function for each valid process * * @param predicate Test if the pid is valid. * @param biConsumer Callback function for each valid pid and its free async space * @param consumer The error function to deal with exceptions */ public void handleFreeAsyncSpace(Predicate predicate, BiConsumer biConsumer, Consumer consumer) { try (ProcFileReader mReader = new ProcFileReader(new FileInputStream(mPath))) { while (mReader.hasMoreData()) { // find the next process if (!mReader.nextString().equals("proc")) { mReader.finishLine(); continue; } // read pid int pid = mReader.nextInt(); mReader.finishLine(); // check if we have interest in this process if (!predicate.test(pid)) { continue; } // read free async space mReader.finishLine(); // context binder mReader.finishLine(); // threads: mReader.finishLine(); // requested threads: mReader.finishLine(); // ready threads if (!mReader.nextString().equals("free")) { mReader.finishLine(); continue; } if (!mReader.nextString().equals("async")) { mReader.finishLine(); continue; } if (!mReader.nextString().equals("space")) { mReader.finishLine(); continue; } int free = mReader.nextInt(); mReader.finishLine(); biConsumer.accept(pid, free); } } catch (IOException | NumberFormatException e) { consumer.accept(e); } } }