• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.Message;
20 import android.os.PowerManagerInternal;
21 import android.os.Process;
22 import android.os.SystemClock;
23 
24 import com.android.internal.annotations.GuardedBy;
25 import com.android.internal.os.BackgroundThread;
26 import com.android.internal.os.ProcessCpuTracker;
27 import com.android.internal.util.RingBuffer;
28 import com.android.internal.util.function.pooled.PooledLambda;
29 
30 import java.io.PrintWriter;
31 
32 public class OomAdjProfiler {
33     private static final int MSG_UPDATE_CPU_TIME = 42;
34 
35     @GuardedBy("this")
36     private boolean mOnBattery;
37     @GuardedBy("this")
38     private boolean mScreenOff;
39 
40     /** The value of {@link #mOnBattery} when the CPU time update was last scheduled. */
41     @GuardedBy("this")
42     private boolean mLastScheduledOnBattery;
43     /** The value of {@link #mScreenOff} when the CPU time update was last scheduled. */
44     @GuardedBy("this")
45     private boolean mLastScheduledScreenOff;
46 
47     @GuardedBy("this")
48     private long mOomAdjStartTimeUs;
49     @GuardedBy("this")
50     private boolean mOomAdjStarted;
51 
52     @GuardedBy("this")
53     private CpuTimes mOomAdjRunTime = new CpuTimes();
54     @GuardedBy("this")
55     private CpuTimes mSystemServerCpuTime = new CpuTimes();
56 
57     @GuardedBy("this")
58     private long mLastSystemServerCpuTimeMs;
59     @GuardedBy("this")
60     private boolean mSystemServerCpuTimeUpdateScheduled;
61     private final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(false);
62 
63     @GuardedBy("this")
64     final RingBuffer<CpuTimes> mOomAdjRunTimesHist = new RingBuffer<>(CpuTimes.class, 10);
65     @GuardedBy("this")
66     final RingBuffer<CpuTimes> mSystemServerCpuTimesHist = new RingBuffer<>(CpuTimes.class, 10);
67 
68     @GuardedBy("this")
69     private long mTotalOomAdjRunTimeUs;
70     @GuardedBy("this")
71     private int mTotalOomAdjCalls;
72 
batteryPowerChanged(boolean onBattery)73     void batteryPowerChanged(boolean onBattery) {
74         synchronized (this) {
75             scheduleSystemServerCpuTimeUpdate();
76             mOnBattery = onBattery;
77         }
78     }
79 
onWakefulnessChanged(int wakefulness)80     void onWakefulnessChanged(int wakefulness) {
81         synchronized (this) {
82             scheduleSystemServerCpuTimeUpdate();
83             mScreenOff = wakefulness != PowerManagerInternal.WAKEFULNESS_AWAKE;
84         }
85     }
86 
oomAdjStarted()87     void oomAdjStarted() {
88         synchronized (this) {
89             mOomAdjStartTimeUs = SystemClock.currentThreadTimeMicro();
90             mOomAdjStarted = true;
91         }
92     }
93 
oomAdjEnded()94     void oomAdjEnded() {
95         synchronized (this) {
96             if (!mOomAdjStarted) {
97                 return;
98             }
99             long elapsedUs = SystemClock.currentThreadTimeMicro() - mOomAdjStartTimeUs;
100             mOomAdjRunTime.addCpuTimeUs(elapsedUs);
101             mTotalOomAdjRunTimeUs += elapsedUs;
102             mTotalOomAdjCalls++;
103         }
104     }
105 
scheduleSystemServerCpuTimeUpdate()106     private void scheduleSystemServerCpuTimeUpdate() {
107         synchronized (this) {
108             if (mSystemServerCpuTimeUpdateScheduled) {
109                 return;
110             }
111             mLastScheduledOnBattery = mOnBattery;
112             mLastScheduledScreenOff = mScreenOff;
113             mSystemServerCpuTimeUpdateScheduled = true;
114             Message scheduledMessage = PooledLambda.obtainMessage(
115                     OomAdjProfiler::updateSystemServerCpuTime,
116                     this, mLastScheduledOnBattery, mLastScheduledScreenOff, true);
117             scheduledMessage.setWhat(MSG_UPDATE_CPU_TIME);
118 
119             BackgroundThread.getHandler().sendMessage(scheduledMessage);
120         }
121     }
122 
updateSystemServerCpuTime(boolean onBattery, boolean screenOff, boolean onlyIfScheduled)123     private void updateSystemServerCpuTime(boolean onBattery, boolean screenOff,
124             boolean onlyIfScheduled) {
125         final long cpuTimeMs = mProcessCpuTracker.getCpuTimeForPid(Process.myPid());
126         synchronized (this) {
127             if (onlyIfScheduled && !mSystemServerCpuTimeUpdateScheduled) {
128                 return;
129             }
130             mSystemServerCpuTime.addCpuTimeMs(
131                     cpuTimeMs - mLastSystemServerCpuTimeMs, onBattery, screenOff);
132             mLastSystemServerCpuTimeMs = cpuTimeMs;
133             mSystemServerCpuTimeUpdateScheduled = false;
134         }
135     }
136 
reset()137     void reset() {
138         synchronized (this) {
139             if (mSystemServerCpuTime.isEmpty()) {
140                 return;
141             }
142             mOomAdjRunTimesHist.append(mOomAdjRunTime);
143             mSystemServerCpuTimesHist.append(mSystemServerCpuTime);
144             mOomAdjRunTime = new CpuTimes();
145             mSystemServerCpuTime = new CpuTimes();
146         }
147     }
148 
dump(PrintWriter pw)149     void dump(PrintWriter pw) {
150         synchronized (this) {
151             if (mSystemServerCpuTimeUpdateScheduled) {
152                 // Cancel the scheduled update since we're going to update it here instead.
153                 BackgroundThread.getHandler().removeMessages(MSG_UPDATE_CPU_TIME);
154                 // Make sure the values are attributed to the right states.
155                 updateSystemServerCpuTime(mLastScheduledOnBattery, mLastScheduledScreenOff, false);
156             } else {
157                 updateSystemServerCpuTime(mOnBattery, mScreenOff, false);
158             }
159 
160             pw.println("System server and oomAdj runtimes (ms) in recent battery sessions "
161                     + "(most recent first):");
162             if (!mSystemServerCpuTime.isEmpty()) {
163                 pw.print("  ");
164                 pw.print("system_server=");
165                 pw.print(mSystemServerCpuTime);
166                 pw.print("  ");
167                 pw.print("oom_adj=");
168                 pw.println(mOomAdjRunTime);
169             }
170             final CpuTimes[] systemServerCpuTimes = mSystemServerCpuTimesHist.toArray();
171             final CpuTimes[] oomAdjRunTimes = mOomAdjRunTimesHist.toArray();
172             for (int i = oomAdjRunTimes.length - 1; i >= 0; --i) {
173                 pw.print("  ");
174                 pw.print("system_server=");
175                 pw.print(systemServerCpuTimes[i]);
176                 pw.print("  ");
177                 pw.print("oom_adj=");
178                 pw.println(oomAdjRunTimes[i]);
179             }
180             if (mTotalOomAdjCalls != 0) {
181                 pw.println("System server total oomAdj runtimes (us) since boot:");
182                 pw.print("  cpu time spent=");
183                 pw.print(mTotalOomAdjRunTimeUs);
184                 pw.print("  number of calls=");
185                 pw.print(mTotalOomAdjCalls);
186                 pw.print("  average=");
187                 pw.println(mTotalOomAdjRunTimeUs / mTotalOomAdjCalls);
188             }
189         }
190     }
191 
192     private class CpuTimes {
193         private long mOnBatteryTimeUs;
194         private long mOnBatteryScreenOffTimeUs;
195 
addCpuTimeMs(long cpuTimeMs)196         public void addCpuTimeMs(long cpuTimeMs) {
197             addCpuTimeUs(cpuTimeMs * 1000, mOnBattery, mScreenOff);
198         }
199 
addCpuTimeMs(long cpuTimeMs, boolean onBattery, boolean screenOff)200         public void addCpuTimeMs(long cpuTimeMs, boolean onBattery, boolean screenOff) {
201             addCpuTimeUs(cpuTimeMs * 1000, onBattery, screenOff);
202         }
203 
addCpuTimeUs(long cpuTimeUs)204         public void addCpuTimeUs(long cpuTimeUs) {
205             addCpuTimeUs(cpuTimeUs, mOnBattery, mScreenOff);
206         }
207 
addCpuTimeUs(long cpuTimeUs, boolean onBattery, boolean screenOff)208         public void addCpuTimeUs(long cpuTimeUs, boolean onBattery, boolean screenOff) {
209             if (onBattery) {
210                 mOnBatteryTimeUs += cpuTimeUs;
211                 if (screenOff) {
212                     mOnBatteryScreenOffTimeUs += cpuTimeUs;
213                 }
214             }
215         }
216 
isEmpty()217         public boolean isEmpty() {
218             return mOnBatteryTimeUs == 0 && mOnBatteryScreenOffTimeUs == 0;
219         }
220 
toString()221         public String toString() {
222             return "[" + (mOnBatteryTimeUs / 1000) + ","
223                     + (mOnBatteryScreenOffTimeUs / 1000) + "]";
224         }
225     }
226 }
227