1 /*
2  * Copyright (C) 2007 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.internal.os;
18 
19 import static android.os.Process.PROC_COMBINE;
20 import static android.os.Process.PROC_OUT_FLOAT;
21 import static android.os.Process.PROC_OUT_LONG;
22 import static android.os.Process.PROC_OUT_STRING;
23 import static android.os.Process.PROC_PARENS;
24 import static android.os.Process.PROC_SPACE_TERM;
25 
26 import android.compat.annotation.UnsupportedAppUsage;
27 import android.os.BatteryStats;
28 import android.os.Build;
29 import android.os.CpuUsageProto;
30 import android.os.Process;
31 import android.os.StrictMode;
32 import android.os.SystemClock;
33 import android.system.ErrnoException;
34 import android.system.Os;
35 import android.system.OsConstants;
36 import android.util.Slog;
37 import android.util.proto.ProtoOutputStream;
38 
39 import com.android.internal.util.FastPrintWriter;
40 
41 import java.io.File;
42 import java.io.FileDescriptor;
43 import java.io.PrintWriter;
44 import java.io.StringWriter;
45 import java.text.SimpleDateFormat;
46 import java.util.ArrayList;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.Date;
50 import java.util.List;
51 
52 public class ProcessCpuTracker {
53     private static final String TAG = "ProcessCpuTracker";
54     private static final boolean DEBUG = false;
55     private static final boolean localLOGV = DEBUG || false;
56 
57     private static final int[] PROCESS_STATS_FORMAT = new int[] {
58         PROC_SPACE_TERM,
59         PROC_SPACE_TERM|PROC_PARENS,
60         PROC_SPACE_TERM,
61         PROC_SPACE_TERM,
62         PROC_SPACE_TERM,
63         PROC_SPACE_TERM,
64         PROC_SPACE_TERM,
65         PROC_SPACE_TERM,
66         PROC_SPACE_TERM,
67         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 10: minor faults
68         PROC_SPACE_TERM,
69         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 12: major faults
70         PROC_SPACE_TERM,
71         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 14: utime
72         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 15: stime
73     };
74 
75     static final int PROCESS_STAT_MINOR_FAULTS = 0;
76     static final int PROCESS_STAT_MAJOR_FAULTS = 1;
77     static final int PROCESS_STAT_UTIME = 2;
78     static final int PROCESS_STAT_STIME = 3;
79 
80     /** Stores user time and system time in jiffies. */
81     private final long[] mProcessStatsData = new long[4];
82 
83     private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
84         PROC_SPACE_TERM,
85         PROC_SPACE_TERM|PROC_PARENS|PROC_OUT_STRING,    // 2: name
86         PROC_SPACE_TERM,
87         PROC_SPACE_TERM,
88         PROC_SPACE_TERM,
89         PROC_SPACE_TERM,
90         PROC_SPACE_TERM,
91         PROC_SPACE_TERM,
92         PROC_SPACE_TERM,
93         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 10: minor faults
94         PROC_SPACE_TERM,
95         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 12: major faults
96         PROC_SPACE_TERM,
97         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 14: utime
98         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 15: stime
99         PROC_SPACE_TERM,
100         PROC_SPACE_TERM,
101         PROC_SPACE_TERM,
102         PROC_SPACE_TERM,
103         PROC_SPACE_TERM,
104         PROC_SPACE_TERM,
105         PROC_SPACE_TERM,
106         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 23: vsize
107     };
108 
109     static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1;
110     static final int PROCESS_FULL_STAT_MAJOR_FAULTS = 2;
111     static final int PROCESS_FULL_STAT_UTIME = 3;
112     static final int PROCESS_FULL_STAT_STIME = 4;
113     static final int PROCESS_FULL_STAT_VSIZE = 5;
114 
115     private final String[] mProcessFullStatsStringData = new String[6];
116     private final long[] mProcessFullStatsData = new long[6];
117 
118     private static final int[] PROCESS_SCHEDSTATS_FORMAT = new int[] {
119             PROC_SPACE_TERM|PROC_OUT_LONG,
120             PROC_SPACE_TERM|PROC_OUT_LONG,
121     };
122 
123     static final int PROCESS_SCHEDSTAT_CPU_TIME = 0;
124     static final int PROCESS_SCHEDSTAT_CPU_DELAY_TIME = 1;
125 
126     private static final int[] SYSTEM_CPU_FORMAT = new int[] {
127         PROC_SPACE_TERM|PROC_COMBINE,
128         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 1: user time
129         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 2: nice time
130         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 3: sys time
131         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 4: idle time
132         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 5: iowait time
133         PROC_SPACE_TERM|PROC_OUT_LONG,                  // 6: irq time
134         PROC_SPACE_TERM|PROC_OUT_LONG                   // 7: softirq time
135     };
136 
137     private final long[] mSystemCpuData = new long[7];
138 
139     private static final int[] LOAD_AVERAGE_FORMAT = new int[] {
140         PROC_SPACE_TERM|PROC_OUT_FLOAT,                 // 0: 1 min
141         PROC_SPACE_TERM|PROC_OUT_FLOAT,                 // 1: 5 mins
142         PROC_SPACE_TERM|PROC_OUT_FLOAT                  // 2: 15 mins
143     };
144 
145     private final float[] mLoadAverageData = new float[3];
146 
147     private final boolean mIncludeThreads;
148 
149     // How long a CPU jiffy is in milliseconds.
150     private final long mJiffyMillis;
151 
152     private float mLoad1 = 0;
153     private float mLoad5 = 0;
154     private float mLoad15 = 0;
155 
156     // All times are in milliseconds. They are converted from jiffies to milliseconds
157     // when extracted from the kernel.
158     private long mCurrentSampleTime;
159     private long mLastSampleTime;
160 
161     private long mCurrentSampleRealTime;
162     private long mLastSampleRealTime;
163 
164     private long mCurrentSampleWallTime;
165     private long mLastSampleWallTime;
166 
167     private long mBaseUserTime;
168     private long mBaseSystemTime;
169     private long mBaseIoWaitTime;
170     private long mBaseIrqTime;
171     private long mBaseSoftIrqTime;
172     private long mBaseIdleTime;
173     private int mRelUserTime;
174     private int mRelSystemTime;
175     private int mRelIoWaitTime;
176     private int mRelIrqTime;
177     private int mRelSoftIrqTime;
178     private int mRelIdleTime;
179     private boolean mRelStatsAreGood;
180 
181     private int[] mCurPids;
182     private int[] mCurThreadPids;
183 
184     private final ArrayList<Stats> mProcStats = new ArrayList<Stats>();
185     private final ArrayList<Stats> mWorkingProcs = new ArrayList<Stats>();
186     private boolean mWorkingProcsSorted;
187 
188     private boolean mFirst = true;
189 
190     public interface FilterStats {
191         /** Which stats to pick when filtering */
needed(Stats stats)192         boolean needed(Stats stats);
193     }
194 
195     public static class Stats {
196         public final int pid;
197         public final int uid;
198         final String statFile;
199         final String cmdlineFile;
200         final String threadsDir;
201         final ArrayList<Stats> threadStats;
202         final ArrayList<Stats> workingThreads;
203 
204         public BatteryStats.Uid.Proc batteryStats;
205 
206         public boolean interesting;
207 
208         public String baseName;
209         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
210         public String name;
211         public int nameWidth;
212 
213         // vsize capture when process first detected; can be used to
214         // filter out kernel processes.
215         public long vsize;
216 
217         /**
218          * Time in milliseconds.
219          */
220         public long base_uptime;
221 
222         /**
223          * Time in milliseconds.
224          */
225         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
226         public long rel_uptime;
227 
228         /**
229          * Time in milliseconds.
230          */
231         public long base_utime;
232 
233         /**
234          * Time in milliseconds.
235          */
236         public long base_stime;
237 
238         /**
239          * Time in milliseconds.
240          */
241         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
242         public int rel_utime;
243 
244         /**
245          * Time in milliseconds.
246          */
247         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
248         public int rel_stime;
249 
250         public long base_minfaults;
251         public long base_majfaults;
252         public int rel_minfaults;
253         public int rel_majfaults;
254 
255         public boolean active;
256         public boolean working;
257         public boolean added;
258         public boolean removed;
259 
Stats(int _pid, int parentPid, boolean includeThreads)260         Stats(int _pid, int parentPid, boolean includeThreads) {
261             pid = _pid;
262             if (parentPid < 0) {
263                 final File procDir = new File("/proc", Integer.toString(pid));
264                 uid = getUid(procDir.toString());
265                 statFile = new File(procDir, "stat").toString();
266                 cmdlineFile = new File(procDir, "cmdline").toString();
267                 threadsDir = (new File(procDir, "task")).toString();
268                 if (includeThreads) {
269                     threadStats = new ArrayList<Stats>();
270                     workingThreads = new ArrayList<Stats>();
271                 } else {
272                     threadStats = null;
273                     workingThreads = null;
274                 }
275             } else {
276                 final File procDir = new File("/proc", Integer.toString(
277                         parentPid));
278                 final File taskDir = new File(
279                         new File(procDir, "task"), Integer.toString(pid));
280                 uid = getUid(taskDir.toString());
281                 statFile = new File(taskDir, "stat").toString();
282                 cmdlineFile = null;
283                 threadsDir = null;
284                 threadStats = null;
285                 workingThreads = null;
286             }
287         }
288 
getUid(String path)289         private static int getUid(String path) {
290             try {
291                 return Os.stat(path).st_uid;
292             } catch (ErrnoException e) {
293                 Slog.w(TAG, "Failed to stat(" + path + "): " + e);
294                 return -1;
295             }
296         }
297     }
298 
299     private final static Comparator<Stats> sLoadComparator = new Comparator<Stats>() {
300         public final int
301         compare(Stats sta, Stats stb) {
302             int ta = sta.rel_utime + sta.rel_stime;
303             int tb = stb.rel_utime + stb.rel_stime;
304             if (ta != tb) {
305                 return ta > tb ? -1 : 1;
306             }
307             if (sta.added != stb.added) {
308                 return sta.added ? -1 : 1;
309             }
310             if (sta.removed != stb.removed) {
311                 return sta.added ? -1 : 1;
312             }
313             return 0;
314         }
315     };
316 
317 
318     @UnsupportedAppUsage
ProcessCpuTracker(boolean includeThreads)319     public ProcessCpuTracker(boolean includeThreads) {
320         mIncludeThreads = includeThreads;
321         long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
322         mJiffyMillis = 1000/jiffyHz;
323     }
324 
onLoadChanged(float load1, float load5, float load15)325     public void onLoadChanged(float load1, float load5, float load15) {
326     }
327 
onMeasureProcessName(String name)328     public int onMeasureProcessName(String name) {
329         return 0;
330     }
331 
init()332     public void init() {
333         if (DEBUG) Slog.v(TAG, "Init: " + this);
334         mFirst = true;
335         update();
336     }
337 
338     @UnsupportedAppUsage
update()339     public void update() {
340         synchronized (this) {
341             updateLocked();
342         }
343     }
344 
updateLocked()345     private void updateLocked() {
346         if (DEBUG) Slog.v(TAG, "Update: " + this);
347 
348         final long nowUptime = SystemClock.uptimeMillis();
349         final long nowRealtime = SystemClock.elapsedRealtime();
350         final long nowWallTime = System.currentTimeMillis();
351 
352         final long[] sysCpu = mSystemCpuData;
353         if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
354                 null, sysCpu, null)) {
355             // Total user time is user + nice time.
356             final long usertime = (sysCpu[0]+sysCpu[1]) * mJiffyMillis;
357             // Total system time is simply system time.
358             final long systemtime = sysCpu[2] * mJiffyMillis;
359             // Total idle time is simply idle time.
360             final long idletime = sysCpu[3] * mJiffyMillis;
361             // Total irq time is iowait + irq + softirq time.
362             final long iowaittime = sysCpu[4] * mJiffyMillis;
363             final long irqtime = sysCpu[5] * mJiffyMillis;
364             final long softirqtime = sysCpu[6] * mJiffyMillis;
365 
366             // This code is trying to avoid issues with idle time going backwards,
367             // but currently it gets into situations where it triggers most of the time. :(
368             if (true || (usertime >= mBaseUserTime && systemtime >= mBaseSystemTime
369                     && iowaittime >= mBaseIoWaitTime && irqtime >= mBaseIrqTime
370                     && softirqtime >= mBaseSoftIrqTime && idletime >= mBaseIdleTime)) {
371                 mRelUserTime = (int)(usertime - mBaseUserTime);
372                 mRelSystemTime = (int)(systemtime - mBaseSystemTime);
373                 mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
374                 mRelIrqTime = (int)(irqtime - mBaseIrqTime);
375                 mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
376                 mRelIdleTime = (int)(idletime - mBaseIdleTime);
377                 mRelStatsAreGood = true;
378 
379                 if (DEBUG) {
380                     Slog.i("Load", "Total U:" + (sysCpu[0]*mJiffyMillis)
381                           + " N:" + (sysCpu[1]*mJiffyMillis)
382                           + " S:" + (sysCpu[2]*mJiffyMillis) + " I:" + (sysCpu[3]*mJiffyMillis)
383                           + " W:" + (sysCpu[4]*mJiffyMillis) + " Q:" + (sysCpu[5]*mJiffyMillis)
384                           + " O:" + (sysCpu[6]*mJiffyMillis));
385                     Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
386                           + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
387                 }
388 
389                 mBaseUserTime = usertime;
390                 mBaseSystemTime = systemtime;
391                 mBaseIoWaitTime = iowaittime;
392                 mBaseIrqTime = irqtime;
393                 mBaseSoftIrqTime = softirqtime;
394                 mBaseIdleTime = idletime;
395 
396             } else {
397                 mRelUserTime = 0;
398                 mRelSystemTime = 0;
399                 mRelIoWaitTime = 0;
400                 mRelIrqTime = 0;
401                 mRelSoftIrqTime = 0;
402                 mRelIdleTime = 0;
403                 mRelStatsAreGood = false;
404                 Slog.w(TAG, "/proc/stats has gone backwards; skipping CPU update");
405                 return;
406             }
407         }
408 
409         mLastSampleTime = mCurrentSampleTime;
410         mCurrentSampleTime = nowUptime;
411         mLastSampleRealTime = mCurrentSampleRealTime;
412         mCurrentSampleRealTime = nowRealtime;
413         mLastSampleWallTime = mCurrentSampleWallTime;
414         mCurrentSampleWallTime = nowWallTime;
415 
416         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
417         try {
418             mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats);
419         } finally {
420             StrictMode.setThreadPolicy(savedPolicy);
421         }
422 
423         final float[] loadAverages = mLoadAverageData;
424         if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT,
425                 null, null, loadAverages)) {
426             float load1 = loadAverages[0];
427             float load5 = loadAverages[1];
428             float load15 = loadAverages[2];
429             if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) {
430                 mLoad1 = load1;
431                 mLoad5 = load5;
432                 mLoad15 = load15;
433                 onLoadChanged(load1, load5, load15);
434             }
435         }
436 
437         if (DEBUG) Slog.i(TAG, "*** TIME TO COLLECT STATS: "
438                 + (SystemClock.uptimeMillis()-mCurrentSampleTime));
439 
440         mWorkingProcsSorted = false;
441         mFirst = false;
442     }
443 
collectStats(String statsFile, int parentPid, boolean first, int[] curPids, ArrayList<Stats> allProcs)444     private int[] collectStats(String statsFile, int parentPid, boolean first,
445             int[] curPids, ArrayList<Stats> allProcs) {
446 
447         int[] pids = Process.getPids(statsFile, curPids);
448         int NP = (pids == null) ? 0 : pids.length;
449         int NS = allProcs.size();
450         int curStatsIndex = 0;
451         for (int i=0; i<NP; i++) {
452             int pid = pids[i];
453             if (pid < 0) {
454                 NP = pid;
455                 break;
456             }
457             Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null;
458 
459             if (st != null && st.pid == pid) {
460                 // Update an existing process...
461                 st.added = false;
462                 st.working = false;
463                 curStatsIndex++;
464                 if (DEBUG) Slog.v(TAG, "Existing "
465                         + (parentPid < 0 ? "process" : "thread")
466                         + " pid " + pid + ": " + st);
467 
468                 if (st.interesting) {
469                     final long uptime = SystemClock.uptimeMillis();
470 
471                     final long[] procStats = mProcessStatsData;
472                     if (!Process.readProcFile(st.statFile.toString(),
473                             PROCESS_STATS_FORMAT, null, procStats, null)) {
474                         continue;
475                     }
476 
477                     final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
478                     final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
479                     final long utime = procStats[PROCESS_STAT_UTIME] * mJiffyMillis;
480                     final long stime = procStats[PROCESS_STAT_STIME] * mJiffyMillis;
481 
482                     if (utime == st.base_utime && stime == st.base_stime) {
483                         st.rel_utime = 0;
484                         st.rel_stime = 0;
485                         st.rel_minfaults = 0;
486                         st.rel_majfaults = 0;
487                         if (st.active) {
488                             st.active = false;
489                         }
490                         continue;
491                     }
492 
493                     if (!st.active) {
494                         st.active = true;
495                     }
496 
497                     if (parentPid < 0) {
498                         getName(st, st.cmdlineFile);
499                         if (st.threadStats != null) {
500                             mCurThreadPids = collectStats(st.threadsDir, pid, false,
501                                     mCurThreadPids, st.threadStats);
502                         }
503                     }
504 
505                     if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
506                             + " utime=" + utime + "-" + st.base_utime
507                             + " stime=" + stime + "-" + st.base_stime
508                             + " minfaults=" + minfaults + "-" + st.base_minfaults
509                             + " majfaults=" + majfaults + "-" + st.base_majfaults);
510 
511                     st.rel_uptime = uptime - st.base_uptime;
512                     st.base_uptime = uptime;
513                     st.rel_utime = (int)(utime - st.base_utime);
514                     st.rel_stime = (int)(stime - st.base_stime);
515                     st.base_utime = utime;
516                     st.base_stime = stime;
517                     st.rel_minfaults = (int)(minfaults - st.base_minfaults);
518                     st.rel_majfaults = (int)(majfaults - st.base_majfaults);
519                     st.base_minfaults = minfaults;
520                     st.base_majfaults = majfaults;
521                     st.working = true;
522                 }
523 
524                 continue;
525             }
526 
527             if (st == null || st.pid > pid) {
528                 // We have a new process!
529                 st = new Stats(pid, parentPid, mIncludeThreads);
530                 allProcs.add(curStatsIndex, st);
531                 curStatsIndex++;
532                 NS++;
533                 if (DEBUG) Slog.v(TAG, "New "
534                         + (parentPid < 0 ? "process" : "thread")
535                         + " pid " + pid + ": " + st);
536 
537                 final String[] procStatsString = mProcessFullStatsStringData;
538                 final long[] procStats = mProcessFullStatsData;
539                 st.base_uptime = SystemClock.uptimeMillis();
540                 String path = st.statFile.toString();
541                 //Slog.d(TAG, "Reading proc file: " + path);
542                 if (Process.readProcFile(path, PROCESS_FULL_STATS_FORMAT, procStatsString,
543                         procStats, null)) {
544                     // This is a possible way to filter out processes that
545                     // are actually kernel threads...  do we want to?  Some
546                     // of them do use CPU, but there can be a *lot* that are
547                     // not doing anything.
548                     st.vsize = procStats[PROCESS_FULL_STAT_VSIZE];
549                     if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) {
550                         st.interesting = true;
551                         st.baseName = procStatsString[0];
552                         st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
553                         st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
554                         st.base_utime = procStats[PROCESS_FULL_STAT_UTIME] * mJiffyMillis;
555                         st.base_stime = procStats[PROCESS_FULL_STAT_STIME] * mJiffyMillis;
556                     } else {
557                         Slog.i(TAG, "Skipping kernel process pid " + pid
558                                 + " name " + procStatsString[0]);
559                         st.baseName = procStatsString[0];
560                     }
561                 } else {
562                     Slog.w(TAG, "Skipping unknown process pid " + pid);
563                     st.baseName = "<unknown>";
564                     st.base_utime = st.base_stime = 0;
565                     st.base_minfaults = st.base_majfaults = 0;
566                 }
567 
568                 if (parentPid < 0) {
569                     getName(st, st.cmdlineFile);
570                     if (st.threadStats != null) {
571                         mCurThreadPids = collectStats(st.threadsDir, pid, true,
572                                 mCurThreadPids, st.threadStats);
573                     }
574                 } else if (st.interesting) {
575                     st.name = st.baseName;
576                     st.nameWidth = onMeasureProcessName(st.name);
577                 }
578 
579                 if (DEBUG) Slog.v("Load", "Stats added " + st.name + " pid=" + st.pid
580                         + " utime=" + st.base_utime + " stime=" + st.base_stime
581                         + " minfaults=" + st.base_minfaults + " majfaults=" + st.base_majfaults);
582 
583                 st.rel_utime = 0;
584                 st.rel_stime = 0;
585                 st.rel_minfaults = 0;
586                 st.rel_majfaults = 0;
587                 st.added = true;
588                 if (!first && st.interesting) {
589                     st.working = true;
590                 }
591                 continue;
592             }
593 
594             // This process has gone away!
595             st.rel_utime = 0;
596             st.rel_stime = 0;
597             st.rel_minfaults = 0;
598             st.rel_majfaults = 0;
599             st.removed = true;
600             st.working = true;
601             allProcs.remove(curStatsIndex);
602             NS--;
603             if (DEBUG) Slog.v(TAG, "Removed "
604                     + (parentPid < 0 ? "process" : "thread")
605                     + " pid " + pid + ": " + st);
606             // Decrement the loop counter so that we process the current pid
607             // again the next time through the loop.
608             i--;
609             continue;
610         }
611 
612         while (curStatsIndex < NS) {
613             // This process has gone away!
614             final Stats st = allProcs.get(curStatsIndex);
615             st.rel_utime = 0;
616             st.rel_stime = 0;
617             st.rel_minfaults = 0;
618             st.rel_majfaults = 0;
619             st.removed = true;
620             st.working = true;
621             allProcs.remove(curStatsIndex);
622             NS--;
623             if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st);
624         }
625 
626         return pids;
627     }
628 
629     /**
630      * Returns the total time (in milliseconds) the given PID has spent
631      * executing in both user and system code. Safe to call without lock held.
632      */
633     public long getCpuTimeForPid(int pid) {
634         final String statFile = "/proc/" + pid + "/stat";
635         final long[] statsData = new long[4];
636         if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
637                 null, statsData, null)) {
638             long time = statsData[PROCESS_STAT_UTIME]
639                         + statsData[PROCESS_STAT_STIME];
640             return time * mJiffyMillis;
641         }
642         return 0;
643     }
644 
645     /**
646      * Returns the total time (in milliseconds) the given PID has spent waiting
647      * in the runqueue. Safe to call without lock held.
648      */
649     public long getCpuDelayTimeForPid(int pid) {
650         final String statFile = "/proc/" + pid + "/schedstat";
651         final long[] statsData = new long[4];
652         if (Process.readProcFile(statFile, PROCESS_SCHEDSTATS_FORMAT,
653                 null, statsData, null)) {
654             return statsData[PROCESS_SCHEDSTAT_CPU_DELAY_TIME] / 1_000_000;
655         }
656         return 0;
657     }
658 
659     /**
660      * @return time in milliseconds.
661      */
662     final public int getLastUserTime() {
663         return mRelUserTime;
664     }
665 
666     /**
667      * @return time in milliseconds.
668      */
669     final public int getLastSystemTime() {
670         return mRelSystemTime;
671     }
672 
673     /**
674      * @return time in milliseconds.
675      */
676     final public int getLastIoWaitTime() {
677         return mRelIoWaitTime;
678     }
679 
680     /**
681      * @return time in milliseconds.
682      */
683     final public int getLastIrqTime() {
684         return mRelIrqTime;
685     }
686 
687     /**
688      * @return time in milliseconds.
689      */
690     final public int getLastSoftIrqTime() {
691         return mRelSoftIrqTime;
692     }
693 
694     /**
695      * @return time in milliseconds.
696      */
697     final public int getLastIdleTime() {
698         return mRelIdleTime;
699     }
700 
701     final public boolean hasGoodLastStats() {
702         return mRelStatsAreGood;
703     }
704 
705     final public float getTotalCpuPercent() {
706         int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime;
707         if (denom <= 0) {
708             return 0;
709         }
710         return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100) / denom;
711     }
712 
713     final void buildWorkingProcs() {
714         if (!mWorkingProcsSorted) {
715             mWorkingProcs.clear();
716             final int N = mProcStats.size();
717             for (int i=0; i<N; i++) {
718                 Stats stats = mProcStats.get(i);
719                 if (stats.working) {
720                     mWorkingProcs.add(stats);
721                     if (stats.threadStats != null && stats.threadStats.size() > 1) {
722                         stats.workingThreads.clear();
723                         final int M = stats.threadStats.size();
724                         for (int j=0; j<M; j++) {
725                             Stats tstats = stats.threadStats.get(j);
726                             if (tstats.working) {
727                                 stats.workingThreads.add(tstats);
728                             }
729                         }
730                         Collections.sort(stats.workingThreads, sLoadComparator);
731                     }
732                 }
733             }
734             Collections.sort(mWorkingProcs, sLoadComparator);
735             mWorkingProcsSorted = true;
736         }
737     }
738 
739     final public int countStats() {
740         return mProcStats.size();
741     }
742 
743     final public Stats getStats(int index) {
744         return mProcStats.get(index);
745     }
746 
747     final public List<Stats> getStats(FilterStats filter) {
748         final ArrayList<Stats> statses = new ArrayList<>(mProcStats.size());
749         final int N = mProcStats.size();
750         for (int p = 0; p < N; p++) {
751             Stats stats = mProcStats.get(p);
752             if (filter.needed(stats)) {
753                 statses.add(stats);
754             }
755         }
756         return statses;
757     }
758 
759     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
760     final public int countWorkingStats() {
761         buildWorkingProcs();
762         return mWorkingProcs.size();
763     }
764 
765     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
766     final public Stats getWorkingStats(int index) {
767         return mWorkingProcs.get(index);
768     }
769 
770     /** Dump cpuinfo in protobuf format. */
771     public final void dumpProto(FileDescriptor fd) {
772         final long now = SystemClock.uptimeMillis();
773         final ProtoOutputStream proto = new ProtoOutputStream(fd);
774         final long currentLoadToken = proto.start(CpuUsageProto.CURRENT_LOAD);
775         proto.write(CpuUsageProto.Load.LOAD1, mLoad1);
776         proto.write(CpuUsageProto.Load.LOAD5, mLoad5);
777         proto.write(CpuUsageProto.Load.LOAD15, mLoad15);
778         proto.end(currentLoadToken);
779 
780         buildWorkingProcs();
781 
782         proto.write(CpuUsageProto.NOW, now);
783         proto.write(CpuUsageProto.LAST_SAMPLE_TIME, mLastSampleTime);
784         proto.write(CpuUsageProto.CURRENT_SAMPLE_TIME, mCurrentSampleTime);
785         proto.write(CpuUsageProto.LAST_SAMPLE_REAL_TIME, mLastSampleRealTime);
786         proto.write(CpuUsageProto.CURRENT_SAMPLE_REAL_TIME, mCurrentSampleRealTime);
787         proto.write(CpuUsageProto.LAST_SAMPLE_WALL_TIME, mLastSampleWallTime);
788         proto.write(CpuUsageProto.CURRENT_SAMPLE_WALL_TIME, mCurrentSampleWallTime);
789 
790         proto.write(CpuUsageProto.TOTAL_USER_TIME, mRelUserTime);
791         proto.write(CpuUsageProto.TOTAL_SYSTEM_TIME, mRelSystemTime);
792         proto.write(CpuUsageProto.TOTAL_IOWAIT_TIME, mRelIoWaitTime);
793         proto.write(CpuUsageProto.TOTAL_IRQ_TIME, mRelIrqTime);
794         proto.write(CpuUsageProto.TOTAL_SOFT_IRQ_TIME, mRelSoftIrqTime);
795         proto.write(CpuUsageProto.TOTAL_IDLE_TIME, mRelIdleTime);
796         final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
797                 + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
798         proto.write(CpuUsageProto.TOTAL_TIME, totalTime);
799 
800         for (Stats st : mWorkingProcs) {
801             dumpProcessCpuProto(proto, st, null);
802             if (!st.removed && st.workingThreads != null) {
803                 for (Stats tst : st.workingThreads) {
804                     dumpProcessCpuProto(proto, tst, st);
805                 }
806             }
807         }
808         proto.flush();
809     }
810 
811     private static void dumpProcessCpuProto(ProtoOutputStream proto, Stats st, Stats proc) {
812         long statToken = proto.start(CpuUsageProto.PROCESSES);
813         proto.write(CpuUsageProto.Stat.UID, st.uid);
814         proto.write(CpuUsageProto.Stat.PID, st.pid);
815         proto.write(CpuUsageProto.Stat.NAME, st.name);
816         proto.write(CpuUsageProto.Stat.ADDED, st.added);
817         proto.write(CpuUsageProto.Stat.REMOVED, st.removed);
818         proto.write(CpuUsageProto.Stat.UPTIME, st.rel_uptime);
819         proto.write(CpuUsageProto.Stat.USER_TIME, st.rel_utime);
820         proto.write(CpuUsageProto.Stat.SYSTEM_TIME, st.rel_stime);
821         proto.write(CpuUsageProto.Stat.MINOR_FAULTS, st.rel_minfaults);
822         proto.write(CpuUsageProto.Stat.MAJOR_FAULTS, st.rel_majfaults);
823         if (proc != null) {
824             proto.write(CpuUsageProto.Stat.PARENT_PID, proc.pid);
825         }
826         proto.end(statToken);
827     }
828 
829     final public String printCurrentLoad() {
830         StringWriter sw = new StringWriter();
831         PrintWriter pw = new FastPrintWriter(sw, false, 128);
832         pw.print("Load: ");
833         pw.print(mLoad1);
834         pw.print(" / ");
835         pw.print(mLoad5);
836         pw.print(" / ");
837         pw.println(mLoad15);
838         pw.flush();
839         return sw.toString();
840     }
841 
842     /**
843      * Returns current CPU state with all the processes as a String, sorted by load
844      * in descending order.
845      */
846     public final String printCurrentState(long now) {
847         return printCurrentState(now, Integer.MAX_VALUE);
848     }
849 
850     /**
851      * Returns current CPU state with the top {@code maxProcessesToDump} highest load
852      * processes as a String, sorted by load in descending order.
853      */
854     public final String printCurrentState(long now, int maxProcessesToDump) {
855         final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
856 
857         buildWorkingProcs();
858 
859         StringWriter sw = new StringWriter();
860         PrintWriter pw = new FastPrintWriter(sw, false, 1024);
861 
862         pw.print("CPU usage from ");
863         if (now > mLastSampleTime) {
864             pw.print(now-mLastSampleTime);
865             pw.print("ms to ");
866             pw.print(now-mCurrentSampleTime);
867             pw.print("ms ago");
868         } else {
869             pw.print(mLastSampleTime-now);
870             pw.print("ms to ");
871             pw.print(mCurrentSampleTime-now);
872             pw.print("ms later");
873         }
874         pw.print(" (");
875         pw.print(sdf.format(new Date(mLastSampleWallTime)));
876         pw.print(" to ");
877         pw.print(sdf.format(new Date(mCurrentSampleWallTime)));
878         pw.print(")");
879 
880         long sampleTime = mCurrentSampleTime - mLastSampleTime;
881         long sampleRealTime = mCurrentSampleRealTime - mLastSampleRealTime;
882         long percAwake = sampleRealTime > 0 ? ((sampleTime*100) / sampleRealTime) : 0;
883         if (percAwake != 100) {
884             pw.print(" with ");
885             pw.print(percAwake);
886             pw.print("% awake");
887         }
888         pw.println(":");
889 
890         final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
891                 + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
892 
893         if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time "
894                 + (mCurrentSampleTime-mLastSampleTime));
895 
896         int dumpedProcessCount = Math.min(maxProcessesToDump, mWorkingProcs.size());
897         for (int i = 0; i < dumpedProcessCount; i++) {
898             Stats st = mWorkingProcs.get(i);
899             printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": "  "),
900                     st.pid, st.name, (int)st.rel_uptime,
901                     st.rel_utime, st.rel_stime, 0, 0, 0, st.rel_minfaults, st.rel_majfaults);
902             if (!st.removed && st.workingThreads != null) {
903                 int M = st.workingThreads.size();
904                 for (int j=0; j<M; j++) {
905                     Stats tst = st.workingThreads.get(j);
906                     printProcessCPU(pw,
907                             tst.added ? "   +" : (tst.removed ? "   -": "    "),
908                             tst.pid, tst.name, (int)st.rel_uptime,
909                             tst.rel_utime, tst.rel_stime, 0, 0, 0, 0, 0);
910                 }
911             }
912         }
913 
914         printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
915                 mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
916 
917         pw.flush();
918         return sw.toString();
919     }
920 
921     private void printRatio(PrintWriter pw, long numerator, long denominator) {
922         long thousands = (numerator*1000)/denominator;
923         long hundreds = thousands/10;
924         pw.print(hundreds);
925         if (hundreds < 10) {
926             long remainder = thousands - (hundreds*10);
927             if (remainder != 0) {
928                 pw.print('.');
929                 pw.print(remainder);
930             }
931         }
932     }
933 
934     private void printProcessCPU(PrintWriter pw, String prefix, int pid, String label,
935             int totalTime, int user, int system, int iowait, int irq, int softIrq,
936             int minFaults, int majFaults) {
937         pw.print(prefix);
938         if (totalTime == 0) totalTime = 1;
939         printRatio(pw, user+system+iowait+irq+softIrq, totalTime);
940         pw.print("% ");
941         if (pid >= 0) {
942             pw.print(pid);
943             pw.print("/");
944         }
945         pw.print(label);
946         pw.print(": ");
947         printRatio(pw, user, totalTime);
948         pw.print("% user + ");
949         printRatio(pw, system, totalTime);
950         pw.print("% kernel");
951         if (iowait > 0) {
952             pw.print(" + ");
953             printRatio(pw, iowait, totalTime);
954             pw.print("% iowait");
955         }
956         if (irq > 0) {
957             pw.print(" + ");
958             printRatio(pw, irq, totalTime);
959             pw.print("% irq");
960         }
961         if (softIrq > 0) {
962             pw.print(" + ");
963             printRatio(pw, softIrq, totalTime);
964             pw.print("% softirq");
965         }
966         if (minFaults > 0 || majFaults > 0) {
967             pw.print(" / faults:");
968             if (minFaults > 0) {
969                 pw.print(" ");
970                 pw.print(minFaults);
971                 pw.print(" minor");
972             }
973             if (majFaults > 0) {
974                 pw.print(" ");
975                 pw.print(majFaults);
976                 pw.print(" major");
977             }
978         }
979         pw.println();
980     }
981 
982     private void getName(Stats st, String cmdlineFile) {
983         String newName = st.name;
984         if (st.name == null
985                 || st.name.equals("app_process")
986                 || st.name.equals("<pre-initialized>")
987                 || st.name.equals("usap32")
988                 || st.name.equals("usap64")) {
989             String cmdName = ProcStatsUtil.readTerminatedProcFile(cmdlineFile, (byte) '\0');
990             if (cmdName != null && cmdName.length() > 1) {
991                 newName = cmdName;
992                 int i = newName.lastIndexOf("/");
993                 if (i > 0 && i < newName.length()-1) {
994                     newName = newName.substring(i+1);
995                 }
996             }
997             if (newName == null) {
998                 newName = st.baseName;
999             }
1000         }
1001         if (st.name == null || !newName.equals(st.name)) {
1002             st.name = newName;
1003             st.nameWidth = onMeasureProcessName(st.name);
1004         }
1005     }
1006 }
1007