1 /*
2  * Copyright (C) 2023 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 
17 package com.android.internal.os;
18 
19 import com.android.internal.util.ProcFileReader;
20 
21 import java.io.FileInputStream;
22 import java.io.IOException;
23 import java.util.function.BiConsumer;
24 import java.util.function.Consumer;
25 import java.util.function.Predicate;
26 
27 /**
28  * Reads and parses {@code binder_logs/stats} file in the {@code binderfs} filesystem.
29  * Reuse procFileReader as the contents are generated by Linux kernel in the same way.
30  *
31  * A typical example of binderfs stats log
32  *
33  * binder stats:
34  * BC_TRANSACTION: 378004
35  * BC_REPLY: 268352
36  * BC_FREE_BUFFER: 665854
37  * ...
38  * proc 12645
39  * context binder
40  * threads: 12
41  * requested threads: 0+5/15
42  * ready threads 0
43  * free async space 520192
44  * ...
45  */
46 @android.ravenwood.annotation.RavenwoodKeepWholeClass
47 public class BinderfsStatsReader {
48     private final String mPath;
49 
BinderfsStatsReader()50     public BinderfsStatsReader() {
51         mPath = "/dev/binderfs/binder_logs/stats";
52     }
53 
BinderfsStatsReader(String path)54     public BinderfsStatsReader(String path) {
55         mPath = path;
56     }
57 
58     /**
59      * Read binderfs stats and call the consumer(pid, free) function for each valid process
60      *
61      * @param predicate  Test if the pid is valid.
62      * @param biConsumer Callback function for each valid pid and its free async space
63      * @param consumer   The error function to deal with exceptions
64      */
handleFreeAsyncSpace(Predicate<Integer> predicate, BiConsumer<Integer, Integer> biConsumer, Consumer<Exception> consumer)65     public void handleFreeAsyncSpace(Predicate<Integer> predicate,
66             BiConsumer<Integer, Integer> biConsumer, Consumer<Exception> consumer) {
67         try (ProcFileReader mReader = new ProcFileReader(new FileInputStream(mPath))) {
68             while (mReader.hasMoreData()) {
69                 // find the next process
70                 if (!mReader.nextString().equals("proc")) {
71                     mReader.finishLine();
72                     continue;
73                 }
74 
75                 // read pid
76                 int pid = mReader.nextInt();
77                 mReader.finishLine();
78 
79                 // check if we have interest in this process
80                 if (!predicate.test(pid)) {
81                     continue;
82                 }
83 
84                 // read free async space
85                 mReader.finishLine(); // context binder
86                 mReader.finishLine(); // threads:
87                 mReader.finishLine(); // requested threads:
88                 mReader.finishLine(); // ready threads
89                 if (!mReader.nextString().equals("free")) {
90                     mReader.finishLine();
91                     continue;
92                 }
93                 if (!mReader.nextString().equals("async")) {
94                     mReader.finishLine();
95                     continue;
96                 }
97                 if (!mReader.nextString().equals("space")) {
98                     mReader.finishLine();
99                     continue;
100                 }
101                 int free = mReader.nextInt();
102                 mReader.finishLine();
103                 biConsumer.accept(pid, free);
104             }
105         } catch (IOException | NumberFormatException e) {
106             consumer.accept(e);
107         }
108     }
109 }
110