1 /*
2  * Copyright (C) 2018 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.server.am;
18 
19 import android.os.PowerManagerInternal;
20 import android.os.Process;
21 import android.os.SystemClock;
22 
23 import com.android.internal.annotations.GuardedBy;
24 import com.android.internal.os.BackgroundThread;
25 import com.android.internal.os.ProcessCpuTracker;
26 import com.android.internal.util.RingBuffer;
27 import com.android.internal.util.function.pooled.PooledLambda;
28 
29 import java.io.PrintWriter;
30 
31 public class OomAdjProfiler {
32     // Disable profiling for Q. Re-enable once b/130635979 is fixed.
33     private static final boolean PROFILING_DISABLED = true;
34 
35     @GuardedBy("this")
36     private boolean mOnBattery;
37     @GuardedBy("this")
38     private boolean mScreenOff;
39 
40     @GuardedBy("this")
41     private long mOomAdjStartTimeMs;
42     @GuardedBy("this")
43     private boolean mOomAdjStarted;
44 
45     @GuardedBy("this")
46     private CpuTimes mOomAdjRunTime = new CpuTimes();
47     @GuardedBy("this")
48     private CpuTimes mSystemServerCpuTime = new CpuTimes();
49 
50     @GuardedBy("this")
51     private long mLastSystemServerCpuTimeMs;
52     @GuardedBy("this")
53     private boolean mSystemServerCpuTimeUpdateScheduled;
54     private final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(false);
55 
56     @GuardedBy("this")
57     final RingBuffer<CpuTimes> mOomAdjRunTimesHist = new RingBuffer<>(CpuTimes.class, 10);
58     @GuardedBy("this")
59     final RingBuffer<CpuTimes> mSystemServerCpuTimesHist = new RingBuffer<>(CpuTimes.class, 10);
60 
batteryPowerChanged(boolean onBattery)61     void batteryPowerChanged(boolean onBattery) {
62         if (PROFILING_DISABLED) {
63             return;
64         }
65         synchronized (this) {
66             scheduleSystemServerCpuTimeUpdate();
67             mOnBattery = onBattery;
68         }
69     }
70 
onWakefulnessChanged(int wakefulness)71     void onWakefulnessChanged(int wakefulness) {
72         if (PROFILING_DISABLED) {
73             return;
74         }
75         synchronized (this) {
76             scheduleSystemServerCpuTimeUpdate();
77             mScreenOff = wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE;
78         }
79     }
80 
oomAdjStarted()81     void oomAdjStarted() {
82         if (PROFILING_DISABLED) {
83             return;
84         }
85         synchronized (this) {
86             mOomAdjStartTimeMs = SystemClock.currentThreadTimeMillis();
87             mOomAdjStarted = true;
88         }
89     }
90 
oomAdjEnded()91     void oomAdjEnded() {
92         if (PROFILING_DISABLED) {
93             return;
94         }
95         synchronized (this) {
96             if (!mOomAdjStarted) {
97                 return;
98             }
99             mOomAdjRunTime.addCpuTimeMs(SystemClock.currentThreadTimeMillis() - mOomAdjStartTimeMs);
100         }
101     }
102 
scheduleSystemServerCpuTimeUpdate()103     private void scheduleSystemServerCpuTimeUpdate() {
104         if (PROFILING_DISABLED) {
105             return;
106         }
107         synchronized (this) {
108             if (mSystemServerCpuTimeUpdateScheduled) {
109                 return;
110             }
111             mSystemServerCpuTimeUpdateScheduled = true;
112             BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
113                     OomAdjProfiler::updateSystemServerCpuTime,
114                     this, mOnBattery, mScreenOff));
115         }
116     }
117 
updateSystemServerCpuTime(boolean onBattery, boolean screenOff)118     private void updateSystemServerCpuTime(boolean onBattery, boolean screenOff) {
119         if (PROFILING_DISABLED) {
120             return;
121         }
122         final long cpuTimeMs = mProcessCpuTracker.getCpuTimeForPid(Process.myPid());
123         synchronized (this) {
124             mSystemServerCpuTime.addCpuTimeMs(
125                     cpuTimeMs - mLastSystemServerCpuTimeMs, onBattery, screenOff);
126             mLastSystemServerCpuTimeMs = cpuTimeMs;
127             mSystemServerCpuTimeUpdateScheduled = false;
128             notifyAll();
129         }
130     }
131 
reset()132     void reset() {
133         synchronized (this) {
134             if (mSystemServerCpuTime.isEmpty()) {
135                 return;
136             }
137             mOomAdjRunTimesHist.append(mOomAdjRunTime);
138             mSystemServerCpuTimesHist.append(mSystemServerCpuTime);
139             mOomAdjRunTime = new CpuTimes();
140             mSystemServerCpuTime = new CpuTimes();
141         }
142     }
143 
dump(PrintWriter pw)144     void dump(PrintWriter pw) {
145         if (PROFILING_DISABLED) {
146             return;
147         }
148         synchronized (this) {
149             if (mSystemServerCpuTimeUpdateScheduled) {
150                 while (mSystemServerCpuTimeUpdateScheduled) {
151                     try {
152                         wait();
153                     } catch (InterruptedException e) {
154                         Thread.currentThread().interrupt();
155                     }
156                 }
157             } else {
158                 updateSystemServerCpuTime(mOnBattery, mScreenOff);
159             }
160 
161             pw.println("System server and oomAdj runtimes (ms) in recent battery sessions "
162                     + "(most recent first):");
163             if (!mSystemServerCpuTime.isEmpty()) {
164                 pw.print("  ");
165                 pw.print("system_server=");
166                 pw.print(mSystemServerCpuTime);
167                 pw.print("  ");
168                 pw.print("oom_adj=");
169                 pw.println(mOomAdjRunTime);
170             }
171             final CpuTimes[] systemServerCpuTimes = mSystemServerCpuTimesHist.toArray();
172             final CpuTimes[] oomAdjRunTimes = mOomAdjRunTimesHist.toArray();
173             for (int i = oomAdjRunTimes.length - 1; i >= 0; --i) {
174                 pw.print("  ");
175                 pw.print("system_server=");
176                 pw.print(systemServerCpuTimes[i]);
177                 pw.print("  ");
178                 pw.print("oom_adj=");
179                 pw.println(oomAdjRunTimes[i]);
180             }
181         }
182     }
183 
184     private class CpuTimes {
185         private long mOnBatteryTimeMs;
186         private long mOnBatteryScreenOffTimeMs;
187 
addCpuTimeMs(long cpuTimeMs)188         public void addCpuTimeMs(long cpuTimeMs) {
189             addCpuTimeMs(cpuTimeMs, mOnBattery, mScreenOff);
190         }
191 
addCpuTimeMs(long cpuTimeMs, boolean onBattery, boolean screenOff)192         public void addCpuTimeMs(long cpuTimeMs, boolean onBattery, boolean screenOff) {
193             if (onBattery) {
194                 mOnBatteryTimeMs += cpuTimeMs;
195                 if (screenOff) {
196                     mOnBatteryScreenOffTimeMs += cpuTimeMs;
197                 }
198             }
199         }
200 
isEmpty()201         public boolean isEmpty() {
202             return mOnBatteryTimeMs == 0 && mOnBatteryScreenOffTimeMs == 0;
203         }
204 
toString()205         public String toString() {
206             return "[" + mOnBatteryTimeMs + "," + mOnBatteryScreenOffTimeMs + "]";
207         }
208     }
209 }
210