1 /* 2 * Copyright (C) 2020 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 static android.os.Process.PROC_NEWLINE_TERM; 20 import static android.os.Process.PROC_OUT_LONG; 21 22 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; 23 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; 24 25 import android.os.Handler; 26 import android.os.Process; 27 import android.os.StrictMode; 28 import android.os.SystemClock; 29 import android.os.Trace; 30 import android.os.UserHandle; 31 import android.text.TextUtils; 32 import android.util.EventLog; 33 import android.util.Slog; 34 import android.util.TimeUtils; 35 36 import com.android.internal.annotations.GuardedBy; 37 38 import java.io.FileDescriptor; 39 import java.io.IOException; 40 import java.io.PrintWriter; 41 import java.util.function.Consumer; 42 43 /** 44 * The "phantom" app processes, which are forked by app processes so we are not aware of 45 * them until we walk through the process list in /proc. 46 */ 47 public final class PhantomProcessRecord { 48 static final String TAG = TAG_WITH_CLASS_NAME ? "PhantomProcessRecord" : TAG_AM; 49 50 static final long[] LONG_OUT = new long[1]; 51 static final int[] LONG_FORMAT = new int[] {PROC_NEWLINE_TERM | PROC_OUT_LONG}; 52 53 final String mProcessName; // name of the process 54 final int mUid; // uid of the process 55 final int mPid; // The id of the process 56 final int mPpid; // Ancestor (managed app process) pid of the process 57 final long mKnownSince; // The timestamp when we're aware of the process 58 final FileDescriptor mPidFd; // The fd to monitor the termination of this process 59 60 long mLastCputime; // How long proc has run CPU at last check 61 long mCurrentCputime; // How long proc has run CPU most recently 62 int mUpdateSeq; // Seq no, indicating the last check on this process 63 int mAdj; // The last known oom adj score 64 boolean mKilled; // Whether it has been killed by us or not 65 boolean mZombie; // Whether it was signaled to be killed but timed out 66 String mStringName; // Caching of the toString() result 67 68 final ActivityManagerService mService; 69 final Object mLock; 70 final Consumer<PhantomProcessRecord> mOnKillListener; 71 final Handler mKillHandler; 72 PhantomProcessRecord(final String processName, final int uid, final int pid, final int ppid, final ActivityManagerService service, final Consumer<PhantomProcessRecord> onKillListener)73 PhantomProcessRecord(final String processName, final int uid, final int pid, 74 final int ppid, final ActivityManagerService service, 75 final Consumer<PhantomProcessRecord> onKillListener) throws IllegalStateException { 76 mProcessName = processName; 77 mUid = uid; 78 mPid = pid; 79 mPpid = ppid; 80 mKilled = false; 81 mAdj = ProcessList.NATIVE_ADJ; 82 mKnownSince = SystemClock.elapsedRealtime(); 83 mService = service; 84 mLock = service.mPhantomProcessList.mLock; 85 mOnKillListener = onKillListener; 86 mKillHandler = service.mProcessList.sKillHandler; 87 if (Process.supportsPidFd()) { 88 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); 89 try { 90 mPidFd = Process.openPidFd(pid, 0); 91 if (mPidFd == null) { 92 throw new IllegalStateException(); 93 } 94 } catch (IOException e) { 95 // Maybe a race condition, the process is gone. 96 Slog.w(TAG, "Unable to open process " + pid + ", it might be gone"); 97 IllegalStateException ex = new IllegalStateException(); 98 ex.initCause(e); 99 throw ex; 100 } finally { 101 StrictMode.setThreadPolicy(oldPolicy); 102 } 103 } else { 104 mPidFd = null; 105 } 106 } 107 108 @GuardedBy("mLock") killLocked(String reason, boolean noisy)109 void killLocked(String reason, boolean noisy) { 110 if (!mKilled) { 111 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill"); 112 if (noisy || mUid == mService.mCurOomAdjUid) { 113 mService.reportUidInfoMessageLocked(TAG, 114 "Killing " + toString() + ": " + reason, mUid); 115 } 116 if (mPid > 0) { 117 EventLog.writeEvent(EventLogTags.AM_KILL, UserHandle.getUserId(mUid), 118 mPid, mProcessName, mAdj, reason); 119 if (!Process.supportsPidFd()) { 120 onProcDied(false); 121 } else { 122 // We'll notify the listener when we're notified it's dead. 123 // Meanwhile, we'd also need handle the case of zombie processes. 124 mKillHandler.postDelayed(mProcKillTimer, this, 125 ProcessList.PROC_KILL_TIMEOUT); 126 } 127 Process.killProcessQuiet(mPid); 128 ProcessList.killProcessGroup(mUid, mPid); 129 } 130 mKilled = true; 131 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 132 } 133 } 134 135 private Runnable mProcKillTimer = new Runnable() { 136 @Override 137 public void run() { 138 synchronized (mLock) { 139 // The process is maybe in either D or Z state. 140 Slog.w(TAG, "Process " + toString() + " is still alive after " 141 + ProcessList.PROC_KILL_TIMEOUT + "ms"); 142 // Force a cleanup as we can't keep the fd open forever 143 mZombie = true; 144 onProcDied(false); 145 // But still bookkeep it, so it won't be added as a new one if it's spotted again. 146 } 147 } 148 }; 149 150 @GuardedBy("mLock") updateAdjLocked()151 void updateAdjLocked() { 152 if (Process.readProcFile("/proc/" + mPid + "/oom_score_adj", 153 LONG_FORMAT, null, LONG_OUT, null)) { 154 mAdj = (int) LONG_OUT[0]; 155 } 156 } 157 158 @GuardedBy("mLock") onProcDied(boolean reallyDead)159 void onProcDied(boolean reallyDead) { 160 if (reallyDead) { 161 Slog.i(TAG, "Process " + toString() + " died"); 162 } 163 mKillHandler.removeCallbacks(mProcKillTimer, this); 164 if (mOnKillListener != null) { 165 mOnKillListener.accept(this); 166 } 167 } 168 169 @Override toString()170 public String toString() { 171 if (mStringName != null) { 172 return mStringName; 173 } 174 StringBuilder sb = new StringBuilder(128); 175 sb.append("PhantomProcessRecord {"); 176 sb.append(Integer.toHexString(System.identityHashCode(this))); 177 sb.append(' '); 178 sb.append(mPid); 179 sb.append(':'); 180 sb.append(mPpid); 181 sb.append(':'); 182 sb.append(mProcessName); 183 sb.append('/'); 184 if (mUid < Process.FIRST_APPLICATION_UID) { 185 sb.append(mUid); 186 } else { 187 sb.append('u'); 188 sb.append(UserHandle.getUserId(mUid)); 189 int appId = UserHandle.getAppId(mUid); 190 if (appId >= Process.FIRST_APPLICATION_UID) { 191 sb.append('a'); 192 sb.append(appId - Process.FIRST_APPLICATION_UID); 193 } else { 194 sb.append('s'); 195 sb.append(appId); 196 } 197 if (appId >= Process.FIRST_ISOLATED_UID && appId <= Process.LAST_ISOLATED_UID) { 198 sb.append('i'); 199 sb.append(appId - Process.FIRST_ISOLATED_UID); 200 } 201 } 202 sb.append('}'); 203 return mStringName = sb.toString(); 204 } 205 dump(PrintWriter pw, String prefix)206 void dump(PrintWriter pw, String prefix) { 207 final long now = SystemClock.elapsedRealtime(); 208 pw.print(prefix); 209 pw.print("user #"); 210 pw.print(UserHandle.getUserId(mUid)); 211 pw.print(" uid="); 212 pw.print(mUid); 213 pw.print(" pid="); 214 pw.print(mPid); 215 pw.print(" ppid="); 216 pw.print(mPpid); 217 pw.print(" knownSince="); 218 TimeUtils.formatDuration(mKnownSince, now, pw); 219 pw.print(" killed="); 220 pw.println(mKilled); 221 pw.print(prefix); 222 pw.print("lastCpuTime="); 223 pw.print(mLastCputime); 224 if (mLastCputime > 0) { 225 pw.print(" timeUsed="); 226 TimeUtils.formatDuration(mCurrentCputime - mLastCputime, pw); 227 } 228 pw.print(" oom adj="); 229 pw.print(mAdj); 230 pw.print(" seq="); 231 pw.println(mUpdateSeq); 232 } 233 equals(final String processName, final int uid, final int pid)234 boolean equals(final String processName, final int uid, final int pid) { 235 return mUid == uid && mPid == pid && TextUtils.equals(mProcessName, processName); 236 } 237 } 238