blob: 0b5d5859e86e7bd64ed1d9f27fedc8bc55d03e6a [file] [log] [blame]
/*
* Copyright (C) 2020 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.server.am;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.Process.FIRST_APPLICATION_UID;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PSS;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import android.annotation.BroadcastBehavior;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
import android.content.ComponentCallbacks2;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsImpl;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.MemInfoReader;
import com.android.server.am.ProcessList.ProcStateMemTracker;
import com.android.server.utils.PriorityDump;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Predicate;
/**
* A helper class taking care of the profiling, memory and cpu sampling of apps
*/
public class AppProfiler {
private static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
static final String TAG_PSS = TAG + POSTFIX_PSS;
static final String TAG_OOM_ADJ = ActivityManagerService.TAG_OOM_ADJ;
/** Control over CPU and battery monitoring */
// write battery stats every 30 minutes.
static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
static final boolean MONITOR_CPU_USAGE = true;
// don't sample cpu less than every 5 seconds.
static final long MONITOR_CPU_MIN_TIME = 5 * 1000;
// wait possibly forever for next cpu sample.
static final long MONITOR_CPU_MAX_TIME = 0x0fffffff;
static final boolean MONITOR_THREAD_CPU_USAGE = false;
static final String ACTIVITY_START_PSS_DEFER_CONFIG = "activity_start_pss_defer";
/**
* Broadcast sent when heap dump collection has been completed.
*/
@BroadcastBehavior(includeBackground = true, protectedBroadcast = true)
private static final String ACTION_HEAP_DUMP_FINISHED =
"com.android.internal.intent.action.HEAP_DUMP_FINISHED";
/**
* The process we are reporting
*/
private static final String EXTRA_HEAP_DUMP_PROCESS_NAME =
"com.android.internal.extra.heap_dump.PROCESS_NAME";
/**
* The size limit the process reached.
*/
private static final String EXTRA_HEAP_DUMP_SIZE_BYTES =
"com.android.internal.extra.heap_dump.SIZE_BYTES";
/**
* Whether the user initiated the dump or not.
*/
private static final String EXTRA_HEAP_DUMP_IS_USER_INITIATED =
"com.android.internal.extra.heap_dump.IS_USER_INITIATED";
/**
* Optional name of package to directly launch.
*/
private static final String EXTRA_HEAP_DUMP_REPORT_PACKAGE =
"com.android.internal.extra.heap_dump.REPORT_PACKAGE";
/**
* How long we defer PSS gathering while activities are starting, in milliseconds.
* This is adjustable via DeviceConfig. If it is zero or negative, no PSS deferral
* is done.
*/
private volatile long mPssDeferralTime = 0;
/**
* Processes we want to collect PSS data from.
*/
@GuardedBy("mService")
private final ArrayList<ProcessRecord> mPendingPssProcesses = new ArrayList<ProcessRecord>();
/**
* Depth of overlapping activity-start PSS deferral notes
*/
private final AtomicInteger mActivityStartingNesting = new AtomicInteger(0);
/**
* Last time we requested PSS data of all processes.
*/
@GuardedBy("mService")
private long mLastFullPssTime = SystemClock.uptimeMillis();
/**
* If set, the next time we collect PSS data we should do a full collection
* with data from native processes and the kernel.
*/
@GuardedBy("mService")
private boolean mFullPssPending = false;
/**
* If true, we are running under a test environment so will sample PSS from processes
* much more rapidly to try to collect better data when the tests are rapidly
* running through apps.
*/
@GuardedBy("mService")
private boolean mTestPssMode = false;
@GuardedBy("mService")
private final LowMemDetector mLowMemDetector;
/**
* Allow the current computed overall memory level of the system to go down?
* This is set to false when we are killing processes for reasons other than
* memory management, so that the now smaller process list will not be taken as
* an indication that memory is tighter.
*/
@GuardedBy("mService")
private boolean mAllowLowerMemLevel = false;
/**
* The last computed memory level, for holding when we are in a state that
* processes are going away for other reasons.
*/
@GuardedBy("mService")
private int mLastMemoryLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
/**
* The last total number of process we have, to determine if changes actually look
* like a shrinking number of process due to lower RAM.
*/
@GuardedBy("mService")
private int mLastNumProcesses;
/**
* Total time spent with RAM that has been added in the past since the last idle time.
*/
@GuardedBy("mService")
private long mLowRamTimeSinceLastIdle = 0;
/**
* If RAM is currently low, when that horrible situation started.
*/
@GuardedBy("mService")
private long mLowRamStartTime = 0;
/**
* Stores a map of process name -> agent string. When a process is started and mAgentAppMap
* is not null, this map is checked and the mapped agent installed during bind-time. Note:
* A non-null agent in mProfileInfo overrides this.
*/
private @Nullable Map<String, String> mAppAgentMap = null;
private int mProfileType = 0;
private final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>();
private String mMemWatchDumpProcName;
private Uri mMemWatchDumpUri;
private int mMemWatchDumpPid;
private int mMemWatchDumpUid;
private boolean mMemWatchIsUserInitiated;
/**
* Used to collect per-process CPU use for ANRs, battery stats, etc.
* Must acquire this object's lock when accessing it.
* NOTE: this lock will be held while doing long operations (trawling
* through all processes in /proc), so it should never be acquired by
* any critical paths such as when holding the main activity manager lock.
*/
private final ProcessCpuTracker mProcessCpuTracker = new ProcessCpuTracker(
MONITOR_THREAD_CPU_USAGE);
private final AtomicLong mLastCpuTime = new AtomicLong(0);
private final AtomicBoolean mProcessCpuMutexFree = new AtomicBoolean(true);
private final CountDownLatch mProcessCpuInitLatch = new CountDownLatch(1);
private long mLastWriteTime = 0;
private final ProfileData mProfileData = new ProfileData();
/**
* Runtime CPU use collection thread. This object's lock is used to
* perform synchronization with the thread (notifying it to run).
*/
private final Thread mProcessCpuThread;
private final ActivityManagerService mService;
private final Handler mBgHandler;
/**
* Observe DeviceConfig changes to the PSS calculation interval
*/
private final DeviceConfig.OnPropertiesChangedListener mPssDelayConfigListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
public void onPropertiesChanged(Properties properties) {
if (properties.getKeyset().contains(ACTIVITY_START_PSS_DEFER_CONFIG)) {
mPssDeferralTime = properties.getLong(ACTIVITY_START_PSS_DEFER_CONFIG, 0);
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Activity-start PSS delay now "
+ mPssDeferralTime + " ms");
}
}
}
};
private class ProfileData {
private String mProfileApp = null;
private ProcessRecord mProfileProc = null;
private ProfilerInfo mProfilerInfo = null;
void setProfileApp(String profileApp) {
mProfileApp = profileApp;
if (mService.mAtmInternal != null) {
mService.mAtmInternal.setProfileApp(profileApp);
}
}
String getProfileApp() {
return mProfileApp;
}
void setProfileProc(ProcessRecord profileProc) {
mProfileProc = profileProc;
if (mService.mAtmInternal != null) {
mService.mAtmInternal.setProfileProc(profileProc == null ? null
: profileProc.getWindowProcessController());
}
}
ProcessRecord getProfileProc() {
return mProfileProc;
}
void setProfilerInfo(ProfilerInfo profilerInfo) {
mProfilerInfo = profilerInfo;
if (mService.mAtmInternal != null) {
mService.mAtmInternal.setProfilerInfo(profilerInfo);
}
}
ProfilerInfo getProfilerInfo() {
return mProfilerInfo;
}
}
private class BgHandler extends Handler {
static final int COLLECT_PSS_BG_MSG = 1;
static final int DEFER_PSS_MSG = 2;
static final int STOP_DEFERRING_PSS_MSG = 3;
BgHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case COLLECT_PSS_BG_MSG:
collectPssInBackground();
break;
case DEFER_PSS_MSG:
deferPssForActivityStart();
break;
case STOP_DEFERRING_PSS_MSG:
stopDeferPss();
break;
}
}
}
private void collectPssInBackground() {
long start = SystemClock.uptimeMillis();
MemInfoReader memInfo = null;
synchronized (mService) {
if (mFullPssPending) {
mFullPssPending = false;
memInfo = new MemInfoReader();
}
}
if (memInfo != null) {
updateCpuStatsNow();
long nativeTotalPss = 0;
final List<ProcessCpuTracker.Stats> stats;
synchronized (mProcessCpuTracker) {
stats = mProcessCpuTracker.getStats(st -> {
return st.vsize > 0 && st.uid < FIRST_APPLICATION_UID;
});
}
final int numOfStats = stats.size();
for (int j = 0; j < numOfStats; j++) {
synchronized (mService.mPidsSelfLocked) {
if (mService.mPidsSelfLocked.indexOfKey(stats.get(j).pid) >= 0) {
// This is one of our own processes; skip it.
continue;
}
}
nativeTotalPss += Debug.getPss(stats.get(j).pid, null, null);
}
memInfo.readMemInfo();
synchronized (mService.mProcessStats.mLock) {
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Collected native and kernel memory in "
+ (SystemClock.uptimeMillis() - start) + "ms");
}
final long cachedKb = memInfo.getCachedSizeKb();
final long freeKb = memInfo.getFreeSizeKb();
final long zramKb = memInfo.getZramTotalSizeKb();
final long kernelKb = memInfo.getKernelUsedSizeKb();
EventLogTags.writeAmMeminfo(cachedKb * 1024, freeKb * 1024, zramKb * 1024,
kernelKb * 1024, nativeTotalPss * 1024);
mService.mProcessStats.addSysMemUsageLocked(cachedKb, freeKb, zramKb, kernelKb,
nativeTotalPss);
}
}
int num = 0;
long[] tmp = new long[3];
do {
ProcessRecord proc;
int procState;
int statType;
int pid = -1;
long lastPssTime;
synchronized (mService) {
if (mPendingPssProcesses.size() <= 0) {
if (mTestPssMode || DEBUG_PSS) {
Slog.d(TAG_PSS,
"Collected pss of " + num + " processes in "
+ (SystemClock.uptimeMillis() - start) + "ms");
}
mPendingPssProcesses.clear();
return;
}
proc = mPendingPssProcesses.remove(0);
procState = proc.pssProcState;
statType = proc.pssStatType;
lastPssTime = proc.lastPssTime;
long now = SystemClock.uptimeMillis();
if (proc.thread != null && procState == proc.setProcState
&& (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE) < now) {
pid = proc.pid;
} else {
abortNextPssTime(proc.procStateMemTracker);
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Skipped pss collection of " + pid
+ ": still need "
+ (lastPssTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE - now)
+ "ms until safe");
}
proc = null;
pid = 0;
}
}
if (proc != null) {
long startTime = SystemClock.currentThreadTimeMillis();
// skip background PSS calculation of apps that are capturing
// camera imagery
final boolean usingCamera = mService.isCameraActiveForUid(proc.uid);
long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
long endTime = SystemClock.currentThreadTimeMillis();
synchronized (mService) {
if (pss != 0 && proc.thread != null && proc.setProcState == procState
&& proc.pid == pid && proc.lastPssTime == lastPssTime) {
num++;
commitNextPssTime(proc.procStateMemTracker);
recordPssSampleLocked(proc, procState, pss, tmp[0], tmp[1], tmp[2],
statType, endTime - startTime, SystemClock.uptimeMillis());
} else {
abortNextPssTime(proc.procStateMemTracker);
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Skipped pss collection of " + pid
+ ": " + (proc.thread == null ? "NO_THREAD " : "")
+ (usingCamera ? "CAMERA " : "")
+ (proc.pid != pid ? "PID_CHANGED " : "")
+ " initState=" + procState + " curState="
+ proc.setProcState + " "
+ (proc.lastPssTime != lastPssTime ? "TIME_CHANGED" : ""));
}
}
}
}
} while (true);
}
private static void commitNextPssTime(ProcStateMemTracker tracker) {
if (tracker.mPendingMemState >= 0) {
tracker.mHighestMem[tracker.mPendingMemState] = tracker.mPendingHighestMemState;
tracker.mScalingFactor[tracker.mPendingMemState] = tracker.mPendingScalingFactor;
tracker.mTotalHighestMem = tracker.mPendingHighestMemState;
tracker.mPendingMemState = -1;
}
}
private static void abortNextPssTime(ProcStateMemTracker tracker) {
tracker.mPendingMemState = -1;
}
@GuardedBy("mService")
void updateNextPssTimeLocked(int procState, ProcessRecord app, long now, boolean forceUpdate) {
if (!forceUpdate) {
if (now <= app.nextPssTime
&& now <= Math.max(app.lastPssTime + ProcessList.PSS_MAX_INTERVAL,
app.lastStateTime + ProcessList.minTimeFromStateChange(mTestPssMode))) {
// update is not due, ignore it.
return;
}
if (!requestPssLocked(app, app.setProcState)) {
return;
}
}
app.nextPssTime = ProcessList.computeNextPssTime(procState, app.procStateMemTracker,
mTestPssMode, mService.mAtmInternal.isSleeping(), now);
}
/**
* Record new PSS sample for a process.
*/
@GuardedBy("mService")
private void recordPssSampleLocked(ProcessRecord proc, int procState, long pss, long uss,
long swapPss, long rss, int statType, long pssDuration, long now) {
EventLogTags.writeAmPss(proc.pid, proc.uid, proc.processName, pss * 1024, uss * 1024,
swapPss * 1024, rss * 1024, statType, procState, pssDuration);
proc.lastPssTime = now;
synchronized (mService.mProcessStats.mLock) {
proc.baseProcessTracker.addPss(
pss, uss, rss, true, statType, pssDuration, proc.pkgList.mPkgList);
}
for (int ipkg = proc.pkgList.mPkgList.size() - 1; ipkg >= 0; ipkg--) {
ProcessStats.ProcessStateHolder holder = proc.pkgList.valueAt(ipkg);
FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_MEMORY_STAT_REPORTED,
proc.info.uid,
holder.state.getName(),
holder.state.getPackage(),
pss, uss, rss, statType, pssDuration,
holder.appVersion);
}
if (DEBUG_PSS) {
Slog.d(TAG_PSS,
"pss of " + proc.toShortString() + ": " + pss + " lastPss=" + proc.lastPss
+ " state=" + ProcessList.makeProcStateString(procState));
}
if (proc.initialIdlePss == 0) {
proc.initialIdlePss = pss;
}
proc.lastPss = pss;
proc.lastSwapPss = swapPss;
if (procState >= ActivityManager.PROCESS_STATE_HOME) {
proc.lastCachedPss = pss;
proc.lastCachedSwapPss = swapPss;
}
proc.mLastRss = rss;
final SparseArray<Pair<Long, String>> watchUids =
mMemWatchProcesses.getMap().get(proc.processName);
Long check = null;
if (watchUids != null) {
Pair<Long, String> val = watchUids.get(proc.uid);
if (val == null) {
val = watchUids.get(0);
}
if (val != null) {
check = val.first;
}
}
if (check != null) {
if ((pss * 1024) >= check && proc.thread != null && mMemWatchDumpProcName == null) {
boolean isDebuggable = Build.IS_DEBUGGABLE;
if (!isDebuggable) {
if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
isDebuggable = true;
}
}
if (isDebuggable) {
Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check + "; reporting");
startHeapDumpLocked(proc, false);
} else {
Slog.w(TAG, "Process " + proc + " exceeded pss limit " + check
+ ", but debugging not enabled");
}
}
}
}
private final class RecordPssRunnable implements Runnable {
private final ProcessRecord mProc;
private final Uri mDumpUri;
private final ContentResolver mContentResolver;
RecordPssRunnable(ProcessRecord proc, Uri dumpUri, ContentResolver contentResolver) {
mProc = proc;
mDumpUri = dumpUri;
mContentResolver = contentResolver;
}
@Override
public void run() {
try (ParcelFileDescriptor fd = mContentResolver.openFileDescriptor(mDumpUri, "rw")) {
IApplicationThread thread = mProc.thread;
if (thread != null) {
try {
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Requesting dump heap from "
+ mProc + " to " + mDumpUri.getPath());
}
thread.dumpHeap(/* managed= */ true,
/* mallocInfo= */ false, /* runGc= */ false,
mDumpUri.getPath(), fd,
/* finishCallback= */ null);
} catch (RemoteException e) {
}
}
} catch (IOException e) {
Slog.e(TAG, "Failed to dump heap", e);
// Need to clear the heap dump variables, otherwise no further heap dumps will be
// attempted.
abortHeapDump(mProc.processName);
}
}
}
@GuardedBy("mService")
void startHeapDumpLocked(ProcessRecord proc, boolean isUserInitiated) {
mMemWatchDumpProcName = proc.processName;
mMemWatchDumpUri = makeHeapDumpUri(proc.processName);
mMemWatchDumpPid = proc.pid;
mMemWatchDumpUid = proc.uid;
mMemWatchIsUserInitiated = isUserInitiated;
Context ctx;
try {
ctx = mService.mContext.createPackageContextAsUser("android", 0,
UserHandle.getUserHandleForUid(mMemWatchDumpUid));
} catch (NameNotFoundException e) {
throw new RuntimeException("android package not found.");
}
BackgroundThread.getHandler().post(
new RecordPssRunnable(proc, mMemWatchDumpUri, ctx.getContentResolver()));
}
void dumpHeapFinished(String path, int callerPid) {
synchronized (mService) {
if (callerPid != mMemWatchDumpPid) {
Slog.w(TAG, "dumpHeapFinished: Calling pid " + Binder.getCallingPid()
+ " does not match last pid " + mMemWatchDumpPid);
return;
}
if (mMemWatchDumpUri == null || !mMemWatchDumpUri.getPath().equals(path)) {
Slog.w(TAG, "dumpHeapFinished: Calling path " + path
+ " does not match last path " + mMemWatchDumpUri);
return;
}
if (DEBUG_PSS) Slog.d(TAG_PSS, "Dump heap finished for " + path);
mService.mHandler.sendEmptyMessage(
ActivityManagerService.POST_DUMP_HEAP_NOTIFICATION_MSG);
// Forced gc to clean up the remnant hprof fd.
Runtime.getRuntime().gc();
}
}
void handlePostDumpHeapNotification() {
final String procName;
final int uid;
final long memLimit;
final String reportPackage;
final boolean isUserInitiated;
synchronized (mService) {
uid = mMemWatchDumpUid;
procName = mMemWatchDumpProcName;
Pair<Long, String> val = mMemWatchProcesses.get(procName, uid);
if (val == null) {
val = mMemWatchProcesses.get(procName, 0);
}
if (val != null) {
memLimit = val.first;
reportPackage = val.second;
} else {
memLimit = 0;
reportPackage = null;
}
isUserInitiated = mMemWatchIsUserInitiated;
mMemWatchDumpUri = null;
mMemWatchDumpProcName = null;
mMemWatchDumpPid = -1;
mMemWatchDumpUid = -1;
}
if (procName == null) {
return;
}
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Showing dump heap notification from " + procName + "/" + uid);
}
Intent dumpFinishedIntent = new Intent(ACTION_HEAP_DUMP_FINISHED);
// Send this only to the Shell package.
dumpFinishedIntent.setPackage("com.android.shell");
dumpFinishedIntent.putExtra(Intent.EXTRA_UID, uid);
dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_IS_USER_INITIATED, isUserInitiated);
dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_SIZE_BYTES, memLimit);
dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_REPORT_PACKAGE, reportPackage);
dumpFinishedIntent.putExtra(EXTRA_HEAP_DUMP_PROCESS_NAME, procName);
mService.mContext.sendBroadcastAsUser(dumpFinishedIntent,
UserHandle.getUserHandleForUid(uid));
}
void setDumpHeapDebugLimit(String processName, int uid, long maxMemSize,
String reportPackage) {
synchronized (mService) {
if (maxMemSize > 0) {
mMemWatchProcesses.put(processName, uid, new Pair(maxMemSize, reportPackage));
} else {
if (uid != 0) {
mMemWatchProcesses.remove(processName, uid);
} else {
mMemWatchProcesses.getMap().remove(processName);
}
}
}
}
/** Clear the currently executing heap dump variables so a new heap dump can be started. */
private void abortHeapDump(String procName) {
Message msg = mService.mHandler.obtainMessage(ActivityManagerService.ABORT_DUMPHEAP_MSG);
msg.obj = procName;
mService.mHandler.sendMessage(msg);
}
void handleAbortDumpHeap(String procName) {
if (procName != null) {
synchronized (mService) {
if (procName.equals(mMemWatchDumpProcName)) {
mMemWatchDumpProcName = null;
mMemWatchDumpUri = null;
mMemWatchDumpPid = -1;
mMemWatchDumpUid = -1;
}
}
}
}
/** @hide */
private static Uri makeHeapDumpUri(String procName) {
return Uri.parse("content://com.android.shell.heapdump/" + procName + "_javaheap.bin");
}
/**
* Schedule PSS collection of a process.
*/
@GuardedBy("mService")
private boolean requestPssLocked(ProcessRecord proc, int procState) {
if (mPendingPssProcesses.contains(proc)) {
return false;
}
if (mPendingPssProcesses.size() == 0) {
final long deferral = (mPssDeferralTime > 0 && mActivityStartingNesting.get() > 0)
? mPssDeferralTime : 0;
if (DEBUG_PSS && deferral > 0) {
Slog.d(TAG_PSS, "requestPssLocked() deferring PSS request by "
+ deferral + " ms");
}
mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, deferral);
}
if (DEBUG_PSS) Slog.d(TAG_PSS, "Requesting pss of: " + proc);
proc.pssProcState = procState;
proc.pssStatType = ProcessStats.ADD_PSS_INTERNAL_SINGLE;
mPendingPssProcesses.add(proc);
return true;
}
/**
* Re-defer a posted PSS collection pass, if one exists. Assumes deferral is
* currently active policy when called.
*/
@GuardedBy("mService")
private void deferPssIfNeededLocked() {
if (mPendingPssProcesses.size() > 0) {
mBgHandler.removeMessages(BgHandler.COLLECT_PSS_BG_MSG);
mBgHandler.sendEmptyMessageDelayed(BgHandler.COLLECT_PSS_BG_MSG, mPssDeferralTime);
}
}
private void deferPssForActivityStart() {
if (mPssDeferralTime > 0) {
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Deferring PSS collection for activity start");
}
deferPssIfNeededLocked();
mActivityStartingNesting.getAndIncrement();
mBgHandler.sendEmptyMessageDelayed(BgHandler.STOP_DEFERRING_PSS_MSG, mPssDeferralTime);
}
}
private void stopDeferPss() {
final int nesting = mActivityStartingNesting.decrementAndGet();
if (nesting <= 0) {
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "PSS activity start deferral interval ended; now "
+ nesting);
}
if (nesting < 0) {
Slog.wtf(TAG, "Activity start nesting undercount!");
mActivityStartingNesting.incrementAndGet();
}
} else {
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Still deferring PSS, nesting=" + nesting);
}
}
}
/**
* Schedule PSS collection of all processes.
*/
@GuardedBy("mService")
void requestPssAllProcsLocked(long now, boolean always, boolean memLowered) {
if (!always) {
if (now < (mLastFullPssTime
+ (memLowered ? mService.mConstants.FULL_PSS_LOWERED_INTERVAL
: mService.mConstants.FULL_PSS_MIN_INTERVAL))) {
return;
}
}
if (DEBUG_PSS) {
Slog.d(TAG_PSS, "Requesting pss of all procs! memLowered=" + memLowered);
}
mLastFullPssTime = now;
mFullPssPending = true;
for (int i = mPendingPssProcesses.size() - 1; i >= 0; i--) {
abortNextPssTime(mPendingPssProcesses.get(i).procStateMemTracker);
}
mPendingPssProcesses.ensureCapacity(mService.mProcessList.getLruSizeLocked());
mPendingPssProcesses.clear();
for (int i = mService.mProcessList.getLruSizeLocked() - 1; i >= 0; i--) {
ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
if (app.thread == null || app.getCurProcState() == PROCESS_STATE_NONEXISTENT) {
continue;
}
if (memLowered || (always && now
> app.lastStateTime + ProcessList.PSS_SAFE_TIME_FROM_STATE_CHANGE)
|| now > (app.lastStateTime + ProcessList.PSS_ALL_INTERVAL)) {
app.pssProcState = app.setProcState;
app.pssStatType = always ? ProcessStats.ADD_PSS_INTERNAL_ALL_POLL
: ProcessStats.ADD_PSS_INTERNAL_ALL_MEM;
updateNextPssTimeLocked(app.getCurProcState(), app, now, true);
mPendingPssProcesses.add(app);
}
}
if (!mBgHandler.hasMessages(BgHandler.COLLECT_PSS_BG_MSG)) {
mBgHandler.sendEmptyMessage(BgHandler.COLLECT_PSS_BG_MSG);
}
}
void setTestPssMode(boolean enabled) {
synchronized (mService) {
mTestPssMode = enabled;
if (enabled) {
// Whenever we enable the mode, we want to take a snapshot all of current
// process mem use.
requestPssAllProcsLocked(SystemClock.uptimeMillis(), true, true);
}
}
}
@GuardedBy("mService")
boolean getTestPssModeLocked() {
return mTestPssMode;
}
@GuardedBy("mService")
int getLastMemoryLevelLocked() {
return mLastMemoryLevel;
}
@GuardedBy("mService")
boolean isLastMemoryLevelNormal() {
return mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL;
}
@GuardedBy("mService")
void updateLowRamTimestampLocked(long now) {
mLowRamTimeSinceLastIdle = 0;
if (mLowRamStartTime != 0) {
mLowRamStartTime = now;
}
}
@GuardedBy("mService")
void setAllowLowerMemLevelLocked(boolean allowLowerMemLevel) {
mAllowLowerMemLevel = allowLowerMemLevel;
}
@GuardedBy("mService")
boolean updateLowMemStateLocked(int numCached, int numEmpty, int numTrimming) {
final int numOfLru = mService.mProcessList.getLruSizeLocked();
final long now = SystemClock.uptimeMillis();
int memFactor;
if (mLowMemDetector != null && mLowMemDetector.isAvailable()) {
memFactor = mLowMemDetector.getMemFactor();
} else {
// Now determine the memory trimming level of background processes.
// Unfortunately we need to start at the back of the list to do this
// properly. We only do this if the number of background apps we
// are managing to keep around is less than half the maximum we desire;
// if we are keeping a good number around, we'll let them use whatever
// memory they want.
if (numCached <= mService.mConstants.CUR_TRIM_CACHED_PROCESSES
&& numEmpty <= mService.mConstants.CUR_TRIM_EMPTY_PROCESSES) {
final int numCachedAndEmpty = numCached + numEmpty;
if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
memFactor = ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
} else if (numCachedAndEmpty <= ProcessList.TRIM_LOW_THRESHOLD) {
memFactor = ProcessStats.ADJ_MEM_FACTOR_LOW;
} else {
memFactor = ProcessStats.ADJ_MEM_FACTOR_MODERATE;
}
} else {
memFactor = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
}
}
// We always allow the memory level to go up (better). We only allow it to go
// down if we are in a state where that is allowed, *and* the total number of processes
// has gone down since last time.
if (DEBUG_OOM_ADJ) {
Slog.d(TAG_OOM_ADJ, "oom: memFactor=" + memFactor
+ " last=" + mLastMemoryLevel + " allowLow=" + mAllowLowerMemLevel
+ " numProcs=" + mService.mProcessList.getLruSizeLocked()
+ " last=" + mLastNumProcesses);
}
if (memFactor > mLastMemoryLevel) {
if (!mAllowLowerMemLevel
|| mService.mProcessList.getLruSizeLocked() >= mLastNumProcesses) {
memFactor = mLastMemoryLevel;
if (DEBUG_OOM_ADJ) Slog.d(TAG_OOM_ADJ, "Keeping last mem factor!");
}
}
if (memFactor != mLastMemoryLevel) {
EventLogTags.writeAmMemFactor(memFactor, mLastMemoryLevel);
FrameworkStatsLog.write(FrameworkStatsLog.MEMORY_FACTOR_STATE_CHANGED, memFactor);
}
mLastMemoryLevel = memFactor;
mLastNumProcesses = mService.mProcessList.getLruSizeLocked();
boolean allChanged;
int trackerMemFactor;
synchronized (mService.mProcessStats.mLock) {
allChanged = mService.mProcessStats.setMemFactorLocked(memFactor,
mService.mAtmInternal == null || !mService.mAtmInternal.isSleeping(), now);
trackerMemFactor = mService.mProcessStats.getMemFactorLocked();
}
if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
if (mLowRamStartTime == 0) {
mLowRamStartTime = now;
}
int step = 0;
int fgTrimLevel;
switch (memFactor) {
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
break;
case ProcessStats.ADJ_MEM_FACTOR_LOW:
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
break;
default:
fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE;
break;
}
int factor = numTrimming / 3;
int minFactor = 2;
if (mService.mAtmInternal.getHomeProcess() != null) minFactor++;
if (mService.mAtmInternal.getPreviousProcess() != null) minFactor++;
if (factor < minFactor) factor = minFactor;
int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
for (int i = numOfLru - 1; i >= 0; i--) {
ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
}
if (app.getCurProcState() >= ActivityManager.PROCESS_STATE_HOME
&& !app.killedByAm) {
if (app.trimMemoryLevel < curLevel && app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ,
"Trimming memory of " + app.processName
+ " to " + curLevel);
}
app.thread.scheduleTrimMemory(curLevel);
} catch (RemoteException e) {
}
}
app.trimMemoryLevel = curLevel;
step++;
if (step >= factor) {
step = 0;
switch (curLevel) {
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
curLevel = ComponentCallbacks2.TRIM_MEMORY_MODERATE;
break;
case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
curLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
break;
}
}
} else if (app.getCurProcState() == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
&& !app.killedByAm) {
if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
&& app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ,
"Trimming memory of heavy-weight " + app.processName
+ " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
}
app.thread.scheduleTrimMemory(
ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
} catch (RemoteException e) {
}
}
app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
} else {
if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
|| app.systemNoUi) && app.hasPendingUiClean()) {
// If this application is now in the background and it
// had done UI, then give it the special trim level to
// have it free UI resources.
final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
if (app.trimMemoryLevel < level && app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
+ app.processName + " to " + level);
}
app.thread.scheduleTrimMemory(level);
} catch (RemoteException e) {
}
}
app.setPendingUiClean(false);
}
if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName
+ " to " + fgTrimLevel);
}
app.thread.scheduleTrimMemory(fgTrimLevel);
} catch (RemoteException e) {
}
}
app.trimMemoryLevel = fgTrimLevel;
}
}
} else {
if (mLowRamStartTime != 0) {
mLowRamTimeSinceLastIdle += now - mLowRamStartTime;
mLowRamStartTime = 0;
}
for (int i = numOfLru - 1; i >= 0; i--) {
ProcessRecord app = mService.mProcessList.mLruProcesses.get(i);
if (allChanged || app.procStateChanged) {
mService.setProcessTrackerStateLocked(app, trackerMemFactor, now);
app.procStateChanged = false;
}
if ((app.getCurProcState() >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
|| app.systemNoUi) && app.hasPendingUiClean()) {
if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
&& app.thread != null) {
try {
if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
Slog.v(TAG_OOM_ADJ,
"Trimming memory of ui hidden " + app.processName
+ " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
}
app.thread.scheduleTrimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
} catch (RemoteException e) {
}
}
app.setPendingUiClean(false);
}
app.trimMemoryLevel = 0;
}
}
return allChanged;
}
@GuardedBy("mService")
long getLowRamTimeSinceIdleLocked(long now) {
return mLowRamTimeSinceLastIdle + (mLowRamStartTime > 0 ? (now - mLowRamStartTime) : 0);
}
@GuardedBy("mService")
private void stopProfilerLocked(ProcessRecord proc, int profileType) {
if (proc == null || proc == mProfileData.getProfileProc()) {
proc = mProfileData.getProfileProc();
profileType = mProfileType;
clearProfilerLocked();
}
if (proc == null) {
return;
}
try {
proc.thread.profilerControl(false, null, profileType);
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
}
}
@GuardedBy("mService")
void clearProfilerLocked() {
if (mProfileData.getProfilerInfo() != null
&& mProfileData.getProfilerInfo().profileFd != null) {
try {
mProfileData.getProfilerInfo().profileFd.close();
} catch (IOException e) {
}
}
mProfileData.setProfileApp(null);
mProfileData.setProfileProc(null);
mProfileData.setProfilerInfo(null);
}
@GuardedBy("mService")
void clearProfilerLocked(ProcessRecord app) {
if (mProfileData.getProfileProc() == null
|| mProfileData.getProfilerInfo() == null
|| mProfileData.getProfileProc() != app) {
return;
}
clearProfilerLocked();
}
@GuardedBy("mService")
boolean profileControlLocked(ProcessRecord proc, boolean start,
ProfilerInfo profilerInfo, int profileType) {
try {
if (start) {
stopProfilerLocked(null, 0);
mService.setProfileApp(proc.info, proc.processName, profilerInfo);
mProfileData.setProfileProc(proc);
mProfileType = profileType;
ParcelFileDescriptor fd = profilerInfo.profileFd;
try {
fd = fd.dup();
} catch (IOException e) {
fd = null;
}
profilerInfo.profileFd = fd;
proc.thread.profilerControl(start, profilerInfo, profileType);
fd = null;
try {
mProfileData.getProfilerInfo().profileFd.close();
} catch (IOException e) {
}
mProfileData.getProfilerInfo().profileFd = null;
if (proc.pid == mService.MY_PID) {
// When profiling the system server itself, avoid closing the file
// descriptor, as profilerControl will not create a copy.
// Note: it is also not correct to just set profileFd to null, as the
// whole ProfilerInfo instance is passed down!
profilerInfo = null;
}
} else {
stopProfilerLocked(proc, profileType);
if (profilerInfo != null && profilerInfo.profileFd != null) {
try {
profilerInfo.profileFd.close();
} catch (IOException e) {
}
}
}
return true;
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
} finally {
if (profilerInfo != null && profilerInfo.profileFd != null) {
try {
profilerInfo.profileFd.close();
} catch (IOException e) {
}
}
}
}
@GuardedBy("mService")
void setProfileAppLocked(String processName, ProfilerInfo profilerInfo) {
mProfileData.setProfileApp(processName);
if (mProfileData.getProfilerInfo() != null) {
if (mProfileData.getProfilerInfo().profileFd != null) {
try {
mProfileData.getProfilerInfo().profileFd.close();
} catch (IOException e) {
}
}
}
mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
mProfileType = 0;
}
@GuardedBy("mService")
void setProfileProcLocked(ProcessRecord proc) {
mProfileData.setProfileProc(proc);
}
@GuardedBy("mService")
void setAgentAppLocked(@NonNull String packageName, @Nullable String agent) {
if (agent == null) {
if (mAppAgentMap != null) {
mAppAgentMap.remove(packageName);
if (mAppAgentMap.isEmpty()) {
mAppAgentMap = null;
}
}
} else {
if (mAppAgentMap == null) {
mAppAgentMap = new HashMap<>();
}
if (mAppAgentMap.size() >= 100) {
// Limit the size of the map, to avoid OOMEs.
Slog.e(TAG, "App agent map has too many entries, cannot add " + packageName
+ "/" + agent);
return;
}
mAppAgentMap.put(packageName, agent);
}
}
@GuardedBy("mService")
void updateCpuStatsLocked() {
final long now = SystemClock.uptimeMillis();
if (mLastCpuTime.get() >= now - MONITOR_CPU_MIN_TIME) {
return;
}
if (mProcessCpuMutexFree.compareAndSet(true, false)) {
synchronized (mProcessCpuThread) {
mProcessCpuThread.notify();
}
}
}
void updateCpuStatsNow() {
synchronized (mProcessCpuTracker) {
mProcessCpuMutexFree.set(false);
final long now = SystemClock.uptimeMillis();
boolean haveNewCpuStats = false;
if (MONITOR_CPU_USAGE
&& mLastCpuTime.get() < (now - MONITOR_CPU_MIN_TIME)) {
mLastCpuTime.set(now);
mProcessCpuTracker.update();
if (mProcessCpuTracker.hasGoodLastStats()) {
haveNewCpuStats = true;
//Slog.i(TAG, mProcessCpu.printCurrentState());
//Slog.i(TAG, "Total CPU usage: "
// + mProcessCpu.getTotalCpuPercent() + "%");
// Slog the cpu usage if the property is set.
if ("true".equals(SystemProperties.get("events.cpu"))) {
int user = mProcessCpuTracker.getLastUserTime();
int system = mProcessCpuTracker.getLastSystemTime();
int iowait = mProcessCpuTracker.getLastIoWaitTime();
int irq = mProcessCpuTracker.getLastIrqTime();
int softIrq = mProcessCpuTracker.getLastSoftIrqTime();
int idle = mProcessCpuTracker.getLastIdleTime();
int total = user + system + iowait + irq + softIrq + idle;
if (total == 0) total = 1;
EventLogTags.writeCpu(
((user + system + iowait + irq + softIrq) * 100) / total,
(user * 100) / total,
(system * 100) / total,
(iowait * 100) / total,
(irq * 100) / total,
(softIrq * 100) / total);
}
}
}
final BatteryStatsImpl bstats = mService.mBatteryStatsService.getActiveStatistics();
synchronized (bstats) {
if (haveNewCpuStats) {
if (bstats.startAddingCpuLocked()) {
int totalUTime = 0;
int totalSTime = 0;
final int statsCount = mProcessCpuTracker.countStats();
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
synchronized (mService.mPidsSelfLocked) {
for (int i = 0; i < statsCount; i++) {
ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(i);
if (!st.working) {
continue;
}
ProcessRecord pr = mService.mPidsSelfLocked.get(st.pid);
totalUTime += st.rel_utime;
totalSTime += st.rel_stime;
if (pr != null) {
BatteryStatsImpl.Uid.Proc ps = pr.curProcBatteryStats;
if (ps == null || !ps.isActive()) {
pr.curProcBatteryStats = ps = bstats.getProcessStatsLocked(
pr.info.uid, pr.processName,
elapsedRealtime, uptime);
}
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
pr.curCpuTime += st.rel_utime + st.rel_stime;
if (pr.lastCpuTime == 0) {
pr.lastCpuTime = pr.curCpuTime;
}
} else {
BatteryStatsImpl.Uid.Proc ps = st.batteryStats;
if (ps == null || !ps.isActive()) {
st.batteryStats = ps = bstats.getProcessStatsLocked(
bstats.mapUid(st.uid), st.name,
elapsedRealtime, uptime);
}
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
}
}
}
final int userTime = mProcessCpuTracker.getLastUserTime();
final int systemTime = mProcessCpuTracker.getLastSystemTime();
final int iowaitTime = mProcessCpuTracker.getLastIoWaitTime();
final int irqTime = mProcessCpuTracker.getLastIrqTime();
final int softIrqTime = mProcessCpuTracker.getLastSoftIrqTime();
final int idleTime = mProcessCpuTracker.getLastIdleTime();
bstats.finishAddingCpuLocked(totalUTime, totalSTime, userTime,
systemTime, iowaitTime, irqTime, softIrqTime, idleTime);
}
}
if (mLastWriteTime < (now - BATTERY_STATS_TIME)) {
mLastWriteTime = now;
mService.mBatteryStatsService.scheduleWriteToDisk();
}
}
}
}
long getCpuTimeForPid(int pid) {
synchronized (mProcessCpuTracker) {
return mProcessCpuTracker.getCpuTimeForPid(pid);
}
}
List<ProcessCpuTracker.Stats> getCpuStats(Predicate<ProcessCpuTracker.Stats> predicate) {
synchronized (mProcessCpuTracker) {
return mProcessCpuTracker.getStats(st -> predicate.test(st));
}
}
void forAllCpuStats(Consumer<ProcessCpuTracker.Stats> consumer) {
synchronized (mProcessCpuTracker) {
final int numOfStats = mProcessCpuTracker.countStats();
for (int i = 0; i < numOfStats; i++) {
consumer.accept(mProcessCpuTracker.getStats(i));
}
}
}
private class ProcessCpuThread extends Thread {
ProcessCpuThread(String name) {
super(name);
}
@Override
public void run() {
synchronized (mProcessCpuTracker) {
mProcessCpuInitLatch.countDown();
mProcessCpuTracker.init();
}
while (true) {
try {
try {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
long nextCpuDelay = (mLastCpuTime.get() + MONITOR_CPU_MAX_TIME) - now;
long nextWriteDelay = (mLastWriteTime + BATTERY_STATS_TIME) - now;
//Slog.i(TAG, "Cpu delay=" + nextCpuDelay
// + ", write delay=" + nextWriteDelay);
if (nextWriteDelay < nextCpuDelay) {
nextCpuDelay = nextWriteDelay;
}
if (nextCpuDelay > 0) {
mProcessCpuMutexFree.set(true);
this.wait(nextCpuDelay);
}
}
} catch (InterruptedException e) {
}
updateCpuStatsNow();
} catch (Exception e) {
Slog.e(TAG, "Unexpected exception collecting process stats", e);
}
}
}
}
class CpuBinder extends Binder {
private final PriorityDump.PriorityDumper mPriorityDumper =
new PriorityDump.PriorityDumper() {
@Override
public void dumpCritical(FileDescriptor fd, PrintWriter pw, String[] args,
boolean asProto) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mService.mContext, "cpuinfo", pw)) {
return;
}
synchronized (mProcessCpuTracker) {
if (asProto) {
mProcessCpuTracker.dumpProto(fd);
return;
}
pw.print(mProcessCpuTracker.printCurrentLoad());
pw.print(mProcessCpuTracker.printCurrentState(
SystemClock.uptimeMillis()));
}
}
};
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
PriorityDump.dump(mPriorityDumper, fd, pw, args);
}
}
void setCpuInfoService() {
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo", new CpuBinder(),
/* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
}
}
AppProfiler(ActivityManagerService service, Looper bgLooper, LowMemDetector detector) {
mService = service;
mBgHandler = new BgHandler(bgLooper);
mLowMemDetector = detector;
mProcessCpuThread = new ProcessCpuThread("CpuTracker");
}
void retrieveSettings() {
final long pssDeferralMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ACTIVITY_START_PSS_DEFER_CONFIG, 0L);
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ActivityThread.currentApplication().getMainExecutor(),
mPssDelayConfigListener);
mPssDeferralTime = pssDeferralMs;
}
void onActivityManagerInternalAdded() {
mProcessCpuThread.start();
// Wait for the synchronized block started in mProcessCpuThread,
// so that any other access to mProcessCpuTracker from main thread
// will be blocked during mProcessCpuTracker initialization.
try {
mProcessCpuInitLatch.await();
} catch (InterruptedException e) {
Slog.wtf(TAG, "Interrupted wait during start", e);
Thread.currentThread().interrupt();
throw new IllegalStateException("Interrupted wait during start");
}
}
void onActivityLaunched() {
// This is safe to force to the head of the queue because it relies only
// on refcounting to track begin/end of deferrals, not on actual
// message ordering. We don't care *what* activity is being
// launched; only that we're doing so.
if (mPssDeferralTime > 0) {
final Message msg = mBgHandler.obtainMessage(BgHandler.DEFER_PSS_MSG);
mBgHandler.sendMessageAtFrontOfQueue(msg);
}
}
@GuardedBy("mService")
ProfilerInfo setupProfilerInfoLocked(@NonNull IApplicationThread thread, ProcessRecord app,
ActiveInstrumentation instr) throws IOException, RemoteException {
ProfilerInfo profilerInfo = null;
String preBindAgent = null;
final String processName = app.processName;
if (mProfileData.getProfileApp() != null
&& mProfileData.getProfileApp().equals(processName)) {
mProfileData.setProfileProc(app);
if (mProfileData.getProfilerInfo() != null) {
// Send a profiler info object to the app if either a file is given, or
// an agent should be loaded at bind-time.
boolean needsInfo = mProfileData.getProfilerInfo().profileFile != null
|| mProfileData.getProfilerInfo().attachAgentDuringBind;
profilerInfo = needsInfo
? new ProfilerInfo(mProfileData.getProfilerInfo()) : null;
if (mProfileData.getProfilerInfo().agent != null) {
preBindAgent = mProfileData.getProfilerInfo().agent;
}
}
} else if (instr != null && instr.mProfileFile != null) {
profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
null, false);
}
if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
// We need to do a debuggable check here. See setAgentApp for why the check is
// postponed to here.
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
String agent = mAppAgentMap.get(processName);
// Do not overwrite already requested agent.
if (profilerInfo == null) {
profilerInfo = new ProfilerInfo(null, null, 0, false, false,
mAppAgentMap.get(processName), true);
} else if (profilerInfo.agent == null) {
profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
}
}
}
if (profilerInfo != null && profilerInfo.profileFd != null) {
profilerInfo.profileFd = profilerInfo.profileFd.dup();
if (TextUtils.equals(mProfileData.getProfileApp(), processName)
&& mProfileData.getProfilerInfo() != null) {
clearProfilerLocked();
}
}
// Check if this is a secondary process that should be incorporated into some
// currently active instrumentation. (Note we do this AFTER all of the profiling
// stuff above because profiling can currently happen only in the primary
// instrumentation process.)
if (mService.mActiveInstrumentation.size() > 0 && instr == null) {
for (int i = mService.mActiveInstrumentation.size() - 1;
i >= 0 && app.getActiveInstrumentation() == null; i--) {
ActiveInstrumentation aInstr = mService.mActiveInstrumentation.get(i);
if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
if (aInstr.mTargetProcesses.length == 0) {
// This is the wildcard mode, where every process brought up for
// the target instrumentation should be included.
if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
app.setActiveInstrumentation(aInstr);
aInstr.mRunningProcesses.add(app);
}
} else {
for (String proc : aInstr.mTargetProcesses) {
if (proc.equals(app.processName)) {
app.setActiveInstrumentation(aInstr);
aInstr.mRunningProcesses.add(app);
break;
}
}
}
}
}
}
// If we were asked to attach an agent on startup, do so now, before we're binding
// application code.
if (preBindAgent != null) {
thread.attachAgent(preBindAgent);
}
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
thread.attachStartupAgents(app.info.dataDir);
}
return profilerInfo;
}
@GuardedBy("mService")
void onCleanupApplicationRecordLocked(ProcessRecord app) {
mPendingPssProcesses.remove(app);
abortNextPssTime(app.procStateMemTracker);
}
@GuardedBy("mService")
void onAppDiedLocked(ProcessRecord app) {
if (mProfileData.getProfileProc() == app) {
clearProfilerLocked();
}
}
@GuardedBy("mService")
boolean dumpMemWatchProcessesLocked(PrintWriter pw, boolean needSep) {
if (mMemWatchProcesses.getMap().size() > 0) {
pw.println(" Mem watch processes:");
final ArrayMap<String, SparseArray<Pair<Long, String>>> procs =
mMemWatchProcesses.getMap();
for (int i = procs.size() - 1; i >= 0; i--) {
final String proc = procs.keyAt(i);
final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
for (int j = uids.size() - 1; j >= 0; j--) {
if (needSep) {
pw.println();
needSep = false;
}
StringBuilder sb = new StringBuilder();
sb.append(" ").append(proc).append('/');
UserHandle.formatUid(sb, uids.keyAt(j));
Pair<Long, String> val = uids.valueAt(j);
sb.append(": "); DebugUtils.sizeValueToString(val.first, sb);
if (val.second != null) {
sb.append(", report to ").append(val.second);
}
pw.println(sb.toString());
}
}
pw.print(" mMemWatchDumpProcName="); pw.println(mMemWatchDumpProcName);
pw.print(" mMemWatchDumpUri="); pw.println(mMemWatchDumpUri);
pw.print(" mMemWatchDumpPid="); pw.println(mMemWatchDumpPid);
pw.print(" mMemWatchDumpUid="); pw.println(mMemWatchDumpUid);
pw.print(" mMemWatchIsUserInitiated="); pw.println(mMemWatchIsUserInitiated);
}
return needSep;
}
@GuardedBy("mService")
boolean dumpProfileDataLocked(PrintWriter pw, String dumpPackage, boolean needSep) {
if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
|| (mProfileData.getProfilerInfo() != null
&& (mProfileData.getProfilerInfo().profileFile != null
|| mProfileData.getProfilerInfo().profileFd != null))) {
if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
if (needSep) {
pw.println();
needSep = false;
}
pw.println(" mProfileApp=" + mProfileData.getProfileApp()
+ " mProfileProc=" + mProfileData.getProfileProc());
if (mProfileData.getProfilerInfo() != null) {
pw.println(" mProfileFile=" + mProfileData.getProfilerInfo().profileFile
+ " mProfileFd=" + mProfileData.getProfilerInfo().profileFd);
pw.println(" mSamplingInterval="
+ mProfileData.getProfilerInfo().samplingInterval
+ " mAutoStopProfiler="
+ mProfileData.getProfilerInfo().autoStopProfiler
+ " mStreamingOutput="
+ mProfileData.getProfilerInfo().streamingOutput);
pw.println(" mProfileType=" + mProfileType);
}
}
}
return needSep;
}
@GuardedBy("mService")
void dumpLastMemoryLevelLocked(PrintWriter pw) {
switch (mLastMemoryLevel) {
case ProcessStats.ADJ_MEM_FACTOR_NORMAL:
pw.println("normal)");
break;
case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
pw.println("moderate)");
break;
case ProcessStats.ADJ_MEM_FACTOR_LOW:
pw.println("low)");
break;
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
pw.println("critical)");
break;
default:
pw.print(mLastMemoryLevel);
pw.println(")");
break;
}
}
@GuardedBy("mService")
void dumpMemoryLevelsLocked(PrintWriter pw) {
pw.println(" mAllowLowerMemLevel=" + mAllowLowerMemLevel
+ " mLastMemoryLevel=" + mLastMemoryLevel
+ " mLastNumProcesses=" + mLastNumProcesses);
}
@GuardedBy("mService")
void writeMemWatchProcessToProtoLocked(ProtoOutputStream proto) {
if (mMemWatchProcesses.getMap().size() > 0) {
final long token = proto.start(
ActivityManagerServiceDumpProcessesProto.MEM_WATCH_PROCESSES);
ArrayMap<String, SparseArray<Pair<Long, String>>> procs = mMemWatchProcesses.getMap();
for (int i = 0; i < procs.size(); i++) {
final String proc = procs.keyAt(i);
final SparseArray<Pair<Long, String>> uids = procs.valueAt(i);
final long ptoken = proto.start(
ActivityManagerServiceDumpProcessesProto.MemWatchProcess.PROCS);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Process.NAME,
proc);
for (int j = uids.size() - 1; j >= 0; j--) {
final long utoken = proto.start(ActivityManagerServiceDumpProcessesProto
.MemWatchProcess.Process.MEM_STATS);
Pair<Long, String> val = uids.valueAt(j);
proto.write(ActivityManagerServiceDumpProcessesProto
.MemWatchProcess.Process.MemStats.UID, uids.keyAt(j));
proto.write(ActivityManagerServiceDumpProcessesProto
.MemWatchProcess.Process.MemStats.SIZE,
DebugUtils.sizeValueToString(val.first, new StringBuilder()));
proto.write(ActivityManagerServiceDumpProcessesProto
.MemWatchProcess.Process.MemStats.REPORT_TO, val.second);
proto.end(utoken);
}
proto.end(ptoken);
}
final long dtoken = proto.start(
ActivityManagerServiceDumpProcessesProto.MemWatchProcess.DUMP);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PROC_NAME,
mMemWatchDumpProcName);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.URI,
mMemWatchDumpUri.toString());
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.PID,
mMemWatchDumpPid);
proto.write(ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.UID,
mMemWatchDumpUid);
proto.write(
ActivityManagerServiceDumpProcessesProto.MemWatchProcess.Dump.IS_USER_INITIATED,
mMemWatchIsUserInitiated);
proto.end(dtoken);
proto.end(token);
}
}
@GuardedBy("mService")
void writeProfileDataToProtoLocked(ProtoOutputStream proto, String dumpPackage) {
if (mProfileData.getProfileApp() != null || mProfileData.getProfileProc() != null
|| (mProfileData.getProfilerInfo() != null
&& (mProfileData.getProfilerInfo().profileFile != null
|| mProfileData.getProfilerInfo().profileFd != null))) {
if (dumpPackage == null || dumpPackage.equals(mProfileData.getProfileApp())) {
final long token = proto.start(ActivityManagerServiceDumpProcessesProto.PROFILE);
proto.write(ActivityManagerServiceDumpProcessesProto.Profile.APP_NAME,
mProfileData.getProfileApp());
mProfileData.getProfileProc().dumpDebug(proto,
ActivityManagerServiceDumpProcessesProto.Profile.PROC);
if (mProfileData.getProfilerInfo() != null) {
mProfileData.getProfilerInfo().dumpDebug(proto,
ActivityManagerServiceDumpProcessesProto.Profile.INFO);
proto.write(ActivityManagerServiceDumpProcessesProto.Profile.TYPE,
mProfileType);
}
proto.end(token);
}
}
}
@GuardedBy("mService")
void writeMemoryLevelsToProtoLocked(ProtoOutputStream proto) {
proto.write(ActivityManagerServiceDumpProcessesProto.ALLOW_LOWER_MEM_LEVEL,
mAllowLowerMemLevel);
proto.write(ActivityManagerServiceDumpProcessesProto.LAST_MEMORY_LEVEL, mLastMemoryLevel);
proto.write(ActivityManagerServiceDumpProcessesProto.LAST_NUM_PROCESSES, mLastNumProcesses);
}
void printCurrentCpuState(StringBuilder report, long time) {
synchronized (mProcessCpuTracker) {
report.append(mProcessCpuTracker.printCurrentState(time));
}
}
}