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