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         if (DEBUG) Slog.v(TAG, "Update: " + this);
341 
342         final long nowUptime = SystemClock.uptimeMillis();
343         final long nowRealtime = SystemClock.elapsedRealtime();
344         final long nowWallTime = System.currentTimeMillis();
345 
346         final long[] sysCpu = mSystemCpuData;
347         if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
348                 null, sysCpu, null)) {
349             // Total user time is user + nice time.
350             final long usertime = (sysCpu[0]+sysCpu[1]) * mJiffyMillis;
351             // Total system time is simply system time.
352             final long systemtime = sysCpu[2] * mJiffyMillis;
353             // Total idle time is simply idle time.
354             final long idletime = sysCpu[3] * mJiffyMillis;
355             // Total irq time is iowait + irq + softirq time.
356             final long iowaittime = sysCpu[4] * mJiffyMillis;
357             final long irqtime = sysCpu[5] * mJiffyMillis;
358             final long softirqtime = sysCpu[6] * mJiffyMillis;
359 
360             // This code is trying to avoid issues with idle time going backwards,
361             // but currently it gets into situations where it triggers most of the time. :(
362             if (true || (usertime >= mBaseUserTime && systemtime >= mBaseSystemTime
363                     && iowaittime >= mBaseIoWaitTime && irqtime >= mBaseIrqTime
364                     && softirqtime >= mBaseSoftIrqTime && idletime >= mBaseIdleTime)) {
365                 mRelUserTime = (int)(usertime - mBaseUserTime);
366                 mRelSystemTime = (int)(systemtime - mBaseSystemTime);
367                 mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
368                 mRelIrqTime = (int)(irqtime - mBaseIrqTime);
369                 mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
370                 mRelIdleTime = (int)(idletime - mBaseIdleTime);
371                 mRelStatsAreGood = true;
372 
373                 if (DEBUG) {
374                     Slog.i("Load", "Total U:" + (sysCpu[0]*mJiffyMillis)
375                           + " N:" + (sysCpu[1]*mJiffyMillis)
376                           + " S:" + (sysCpu[2]*mJiffyMillis) + " I:" + (sysCpu[3]*mJiffyMillis)
377                           + " W:" + (sysCpu[4]*mJiffyMillis) + " Q:" + (sysCpu[5]*mJiffyMillis)
378                           + " O:" + (sysCpu[6]*mJiffyMillis));
379                     Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
380                           + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
381                 }
382 
383                 mBaseUserTime = usertime;
384                 mBaseSystemTime = systemtime;
385                 mBaseIoWaitTime = iowaittime;
386                 mBaseIrqTime = irqtime;
387                 mBaseSoftIrqTime = softirqtime;
388                 mBaseIdleTime = idletime;
389 
390             } else {
391                 mRelUserTime = 0;
392                 mRelSystemTime = 0;
393                 mRelIoWaitTime = 0;
394                 mRelIrqTime = 0;
395                 mRelSoftIrqTime = 0;
396                 mRelIdleTime = 0;
397                 mRelStatsAreGood = false;
398                 Slog.w(TAG, "/proc/stats has gone backwards; skipping CPU update");
399                 return;
400             }
401         }
402 
403         mLastSampleTime = mCurrentSampleTime;
404         mCurrentSampleTime = nowUptime;
405         mLastSampleRealTime = mCurrentSampleRealTime;
406         mCurrentSampleRealTime = nowRealtime;
407         mLastSampleWallTime = mCurrentSampleWallTime;
408         mCurrentSampleWallTime = nowWallTime;
409 
410         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
411         try {
412             mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats);
413         } finally {
414             StrictMode.setThreadPolicy(savedPolicy);
415         }
416 
417         final float[] loadAverages = mLoadAverageData;
418         if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT,
419                 null, null, loadAverages)) {
420             float load1 = loadAverages[0];
421             float load5 = loadAverages[1];
422             float load15 = loadAverages[2];
423             if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) {
424                 mLoad1 = load1;
425                 mLoad5 = load5;
426                 mLoad15 = load15;
427                 onLoadChanged(load1, load5, load15);
428             }
429         }
430 
431         if (DEBUG) Slog.i(TAG, "*** TIME TO COLLECT STATS: "
432                 + (SystemClock.uptimeMillis()-mCurrentSampleTime));
433 
434         mWorkingProcsSorted = false;
435         mFirst = false;
436     }
437 
collectStats(String statsFile, int parentPid, boolean first, int[] curPids, ArrayList<Stats> allProcs)438     private int[] collectStats(String statsFile, int parentPid, boolean first,
439             int[] curPids, ArrayList<Stats> allProcs) {
440 
441         int[] pids = Process.getPids(statsFile, curPids);
442         int NP = (pids == null) ? 0 : pids.length;
443         int NS = allProcs.size();
444         int curStatsIndex = 0;
445         for (int i=0; i<NP; i++) {
446             int pid = pids[i];
447             if (pid < 0) {
448                 NP = pid;
449                 break;
450             }
451             Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null;
452 
453             if (st != null && st.pid == pid) {
454                 // Update an existing process...
455                 st.added = false;
456                 st.working = false;
457                 curStatsIndex++;
458                 if (DEBUG) Slog.v(TAG, "Existing "
459                         + (parentPid < 0 ? "process" : "thread")
460                         + " pid " + pid + ": " + st);
461 
462                 if (st.interesting) {
463                     final long uptime = SystemClock.uptimeMillis();
464 
465                     final long[] procStats = mProcessStatsData;
466                     if (!Process.readProcFile(st.statFile.toString(),
467                             PROCESS_STATS_FORMAT, null, procStats, null)) {
468                         continue;
469                     }
470 
471                     final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS];
472                     final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS];
473                     final long utime = procStats[PROCESS_STAT_UTIME] * mJiffyMillis;
474                     final long stime = procStats[PROCESS_STAT_STIME] * mJiffyMillis;
475 
476                     if (utime == st.base_utime && stime == st.base_stime) {
477                         st.rel_utime = 0;
478                         st.rel_stime = 0;
479                         st.rel_minfaults = 0;
480                         st.rel_majfaults = 0;
481                         if (st.active) {
482                             st.active = false;
483                         }
484                         continue;
485                     }
486 
487                     if (!st.active) {
488                         st.active = true;
489                     }
490 
491                     if (parentPid < 0) {
492                         getName(st, st.cmdlineFile);
493                         if (st.threadStats != null) {
494                             mCurThreadPids = collectStats(st.threadsDir, pid, false,
495                                     mCurThreadPids, st.threadStats);
496                         }
497                     }
498 
499                     if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid
500                             + " utime=" + utime + "-" + st.base_utime
501                             + " stime=" + stime + "-" + st.base_stime
502                             + " minfaults=" + minfaults + "-" + st.base_minfaults
503                             + " majfaults=" + majfaults + "-" + st.base_majfaults);
504 
505                     st.rel_uptime = uptime - st.base_uptime;
506                     st.base_uptime = uptime;
507                     st.rel_utime = (int)(utime - st.base_utime);
508                     st.rel_stime = (int)(stime - st.base_stime);
509                     st.base_utime = utime;
510                     st.base_stime = stime;
511                     st.rel_minfaults = (int)(minfaults - st.base_minfaults);
512                     st.rel_majfaults = (int)(majfaults - st.base_majfaults);
513                     st.base_minfaults = minfaults;
514                     st.base_majfaults = majfaults;
515                     st.working = true;
516                 }
517 
518                 continue;
519             }
520 
521             if (st == null || st.pid > pid) {
522                 // We have a new process!
523                 st = new Stats(pid, parentPid, mIncludeThreads);
524                 allProcs.add(curStatsIndex, st);
525                 curStatsIndex++;
526                 NS++;
527                 if (DEBUG) Slog.v(TAG, "New "
528                         + (parentPid < 0 ? "process" : "thread")
529                         + " pid " + pid + ": " + st);
530 
531                 final String[] procStatsString = mProcessFullStatsStringData;
532                 final long[] procStats = mProcessFullStatsData;
533                 st.base_uptime = SystemClock.uptimeMillis();
534                 String path = st.statFile.toString();
535                 //Slog.d(TAG, "Reading proc file: " + path);
536                 if (Process.readProcFile(path, PROCESS_FULL_STATS_FORMAT, procStatsString,
537                         procStats, null)) {
538                     // This is a possible way to filter out processes that
539                     // are actually kernel threads...  do we want to?  Some
540                     // of them do use CPU, but there can be a *lot* that are
541                     // not doing anything.
542                     st.vsize = procStats[PROCESS_FULL_STAT_VSIZE];
543                     if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) {
544                         st.interesting = true;
545                         st.baseName = procStatsString[0];
546                         st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS];
547                         st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS];
548                         st.base_utime = procStats[PROCESS_FULL_STAT_UTIME] * mJiffyMillis;
549                         st.base_stime = procStats[PROCESS_FULL_STAT_STIME] * mJiffyMillis;
550                     } else {
551                         Slog.i(TAG, "Skipping kernel process pid " + pid
552                                 + " name " + procStatsString[0]);
553                         st.baseName = procStatsString[0];
554                     }
555                 } else {
556                     Slog.w(TAG, "Skipping unknown process pid " + pid);
557                     st.baseName = "<unknown>";
558                     st.base_utime = st.base_stime = 0;
559                     st.base_minfaults = st.base_majfaults = 0;
560                 }
561 
562                 if (parentPid < 0) {
563                     getName(st, st.cmdlineFile);
564                     if (st.threadStats != null) {
565                         mCurThreadPids = collectStats(st.threadsDir, pid, true,
566                                 mCurThreadPids, st.threadStats);
567                     }
568                 } else if (st.interesting) {
569                     st.name = st.baseName;
570                     st.nameWidth = onMeasureProcessName(st.name);
571                 }
572 
573                 if (DEBUG) Slog.v("Load", "Stats added " + st.name + " pid=" + st.pid
574                         + " utime=" + st.base_utime + " stime=" + st.base_stime
575                         + " minfaults=" + st.base_minfaults + " majfaults=" + st.base_majfaults);
576 
577                 st.rel_utime = 0;
578                 st.rel_stime = 0;
579                 st.rel_minfaults = 0;
580                 st.rel_majfaults = 0;
581                 st.added = true;
582                 if (!first && st.interesting) {
583                     st.working = true;
584                 }
585                 continue;
586             }
587 
588             // This process has gone away!
589             st.rel_utime = 0;
590             st.rel_stime = 0;
591             st.rel_minfaults = 0;
592             st.rel_majfaults = 0;
593             st.removed = true;
594             st.working = true;
595             allProcs.remove(curStatsIndex);
596             NS--;
597             if (DEBUG) Slog.v(TAG, "Removed "
598                     + (parentPid < 0 ? "process" : "thread")
599                     + " pid " + pid + ": " + st);
600             // Decrement the loop counter so that we process the current pid
601             // again the next time through the loop.
602             i--;
603             continue;
604         }
605 
606         while (curStatsIndex < NS) {
607             // This process has gone away!
608             final Stats st = allProcs.get(curStatsIndex);
609             st.rel_utime = 0;
610             st.rel_stime = 0;
611             st.rel_minfaults = 0;
612             st.rel_majfaults = 0;
613             st.removed = true;
614             st.working = true;
615             allProcs.remove(curStatsIndex);
616             NS--;
617             if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st);
618         }
619 
620         return pids;
621     }
622 
623     /**
624      * Returns the total time (in milliseconds) the given PID has spent
625      * executing in both user and system code. Safe to call without lock held.
626      */
627     public long getCpuTimeForPid(int pid) {
628         final String statFile = "/proc/" + pid + "/stat";
629         final long[] statsData = new long[4];
630         if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
631                 null, statsData, null)) {
632             long time = statsData[PROCESS_STAT_UTIME]
633                         + statsData[PROCESS_STAT_STIME];
634             return time * mJiffyMillis;
635         }
636         return 0;
637     }
638 
639     /**
640      * Returns the total time (in milliseconds) the given PID has spent waiting
641      * in the runqueue. Safe to call without lock held.
642      */
643     public long getCpuDelayTimeForPid(int pid) {
644         final String statFile = "/proc/" + pid + "/schedstat";
645         final long[] statsData = new long[4];
646         if (Process.readProcFile(statFile, PROCESS_SCHEDSTATS_FORMAT,
647                 null, statsData, null)) {
648             return statsData[PROCESS_SCHEDSTAT_CPU_DELAY_TIME] / 1_000_000;
649         }
650         return 0;
651     }
652 
653     /**
654      * @return time in milliseconds.
655      */
656     final public int getLastUserTime() {
657         return mRelUserTime;
658     }
659 
660     /**
661      * @return time in milliseconds.
662      */
663     final public int getLastSystemTime() {
664         return mRelSystemTime;
665     }
666 
667     /**
668      * @return time in milliseconds.
669      */
670     final public int getLastIoWaitTime() {
671         return mRelIoWaitTime;
672     }
673 
674     /**
675      * @return time in milliseconds.
676      */
677     final public int getLastIrqTime() {
678         return mRelIrqTime;
679     }
680 
681     /**
682      * @return time in milliseconds.
683      */
684     final public int getLastSoftIrqTime() {
685         return mRelSoftIrqTime;
686     }
687 
688     /**
689      * @return time in milliseconds.
690      */
691     final public int getLastIdleTime() {
692         return mRelIdleTime;
693     }
694 
695     final public boolean hasGoodLastStats() {
696         return mRelStatsAreGood;
697     }
698 
699     final public float getTotalCpuPercent() {
700         int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime;
701         if (denom <= 0) {
702             return 0;
703         }
704         return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100) / denom;
705     }
706 
707     final void buildWorkingProcs() {
708         if (!mWorkingProcsSorted) {
709             mWorkingProcs.clear();
710             final int N = mProcStats.size();
711             for (int i=0; i<N; i++) {
712                 Stats stats = mProcStats.get(i);
713                 if (stats.working) {
714                     mWorkingProcs.add(stats);
715                     if (stats.threadStats != null && stats.threadStats.size() > 1) {
716                         stats.workingThreads.clear();
717                         final int M = stats.threadStats.size();
718                         for (int j=0; j<M; j++) {
719                             Stats tstats = stats.threadStats.get(j);
720                             if (tstats.working) {
721                                 stats.workingThreads.add(tstats);
722                             }
723                         }
724                         Collections.sort(stats.workingThreads, sLoadComparator);
725                     }
726                 }
727             }
728             Collections.sort(mWorkingProcs, sLoadComparator);
729             mWorkingProcsSorted = true;
730         }
731     }
732 
733     final public int countStats() {
734         return mProcStats.size();
735     }
736 
737     final public Stats getStats(int index) {
738         return mProcStats.get(index);
739     }
740 
741     final public List<Stats> getStats(FilterStats filter) {
742         final ArrayList<Stats> statses = new ArrayList<>(mProcStats.size());
743         final int N = mProcStats.size();
744         for (int p = 0; p < N; p++) {
745             Stats stats = mProcStats.get(p);
746             if (filter.needed(stats)) {
747                 statses.add(stats);
748             }
749         }
750         return statses;
751     }
752 
753     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
754     final public int countWorkingStats() {
755         buildWorkingProcs();
756         return mWorkingProcs.size();
757     }
758 
759     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
760     final public Stats getWorkingStats(int index) {
761         return mWorkingProcs.get(index);
762     }
763 
764     /** Dump cpuinfo in protobuf format. */
765     public final void dumpProto(FileDescriptor fd) {
766         final long now = SystemClock.uptimeMillis();
767         final ProtoOutputStream proto = new ProtoOutputStream(fd);
768         final long currentLoadToken = proto.start(CpuUsageProto.CURRENT_LOAD);
769         proto.write(CpuUsageProto.Load.LOAD1, mLoad1);
770         proto.write(CpuUsageProto.Load.LOAD5, mLoad5);
771         proto.write(CpuUsageProto.Load.LOAD15, mLoad15);
772         proto.end(currentLoadToken);
773 
774         buildWorkingProcs();
775 
776         proto.write(CpuUsageProto.NOW, now);
777         proto.write(CpuUsageProto.LAST_SAMPLE_TIME, mLastSampleTime);
778         proto.write(CpuUsageProto.CURRENT_SAMPLE_TIME, mCurrentSampleTime);
779         proto.write(CpuUsageProto.LAST_SAMPLE_REAL_TIME, mLastSampleRealTime);
780         proto.write(CpuUsageProto.CURRENT_SAMPLE_REAL_TIME, mCurrentSampleRealTime);
781         proto.write(CpuUsageProto.LAST_SAMPLE_WALL_TIME, mLastSampleWallTime);
782         proto.write(CpuUsageProto.CURRENT_SAMPLE_WALL_TIME, mCurrentSampleWallTime);
783 
784         proto.write(CpuUsageProto.TOTAL_USER_TIME, mRelUserTime);
785         proto.write(CpuUsageProto.TOTAL_SYSTEM_TIME, mRelSystemTime);
786         proto.write(CpuUsageProto.TOTAL_IOWAIT_TIME, mRelIoWaitTime);
787         proto.write(CpuUsageProto.TOTAL_IRQ_TIME, mRelIrqTime);
788         proto.write(CpuUsageProto.TOTAL_SOFT_IRQ_TIME, mRelSoftIrqTime);
789         proto.write(CpuUsageProto.TOTAL_IDLE_TIME, mRelIdleTime);
790         final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
791                 + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
792         proto.write(CpuUsageProto.TOTAL_TIME, totalTime);
793 
794         for (Stats st : mWorkingProcs) {
795             dumpProcessCpuProto(proto, st, null);
796             if (!st.removed && st.workingThreads != null) {
797                 for (Stats tst : st.workingThreads) {
798                     dumpProcessCpuProto(proto, tst, st);
799                 }
800             }
801         }
802         proto.flush();
803     }
804 
805     private static void dumpProcessCpuProto(ProtoOutputStream proto, Stats st, Stats proc) {
806         long statToken = proto.start(CpuUsageProto.PROCESSES);
807         proto.write(CpuUsageProto.Stat.UID, st.uid);
808         proto.write(CpuUsageProto.Stat.PID, st.pid);
809         proto.write(CpuUsageProto.Stat.NAME, st.name);
810         proto.write(CpuUsageProto.Stat.ADDED, st.added);
811         proto.write(CpuUsageProto.Stat.REMOVED, st.removed);
812         proto.write(CpuUsageProto.Stat.UPTIME, st.rel_uptime);
813         proto.write(CpuUsageProto.Stat.USER_TIME, st.rel_utime);
814         proto.write(CpuUsageProto.Stat.SYSTEM_TIME, st.rel_stime);
815         proto.write(CpuUsageProto.Stat.MINOR_FAULTS, st.rel_minfaults);
816         proto.write(CpuUsageProto.Stat.MAJOR_FAULTS, st.rel_majfaults);
817         if (proc != null) {
818             proto.write(CpuUsageProto.Stat.PARENT_PID, proc.pid);
819         }
820         proto.end(statToken);
821     }
822 
823     final public String printCurrentLoad() {
824         StringWriter sw = new StringWriter();
825         PrintWriter pw = new FastPrintWriter(sw, false, 128);
826         pw.print("Load: ");
827         pw.print(mLoad1);
828         pw.print(" / ");
829         pw.print(mLoad5);
830         pw.print(" / ");
831         pw.println(mLoad15);
832         pw.flush();
833         return sw.toString();
834     }
835 
836     /**
837      * Returns current CPU state with all the processes as a String, sorted by load
838      * in descending order.
839      */
840     public final String printCurrentState(long now) {
841         return printCurrentState(now, Integer.MAX_VALUE);
842     }
843 
844     /**
845      * Returns current CPU state with the top {@code maxProcessesToDump} highest load
846      * processes as a String, sorted by load in descending order.
847      */
848     public final String printCurrentState(long now, int maxProcessesToDump) {
849         final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
850 
851         buildWorkingProcs();
852 
853         StringWriter sw = new StringWriter();
854         PrintWriter pw = new FastPrintWriter(sw, false, 1024);
855 
856         pw.print("CPU usage from ");
857         if (now > mLastSampleTime) {
858             pw.print(now-mLastSampleTime);
859             pw.print("ms to ");
860             pw.print(now-mCurrentSampleTime);
861             pw.print("ms ago");
862         } else {
863             pw.print(mLastSampleTime-now);
864             pw.print("ms to ");
865             pw.print(mCurrentSampleTime-now);
866             pw.print("ms later");
867         }
868         pw.print(" (");
869         pw.print(sdf.format(new Date(mLastSampleWallTime)));
870         pw.print(" to ");
871         pw.print(sdf.format(new Date(mCurrentSampleWallTime)));
872         pw.print(")");
873 
874         long sampleTime = mCurrentSampleTime - mLastSampleTime;
875         long sampleRealTime = mCurrentSampleRealTime - mLastSampleRealTime;
876         long percAwake = sampleRealTime > 0 ? ((sampleTime*100) / sampleRealTime) : 0;
877         if (percAwake != 100) {
878             pw.print(" with ");
879             pw.print(percAwake);
880             pw.print("% awake");
881         }
882         pw.println(":");
883 
884         final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime
885                 + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime;
886 
887         if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time "
888                 + (mCurrentSampleTime-mLastSampleTime));
889 
890         int dumpedProcessCount = Math.min(maxProcessesToDump, mWorkingProcs.size());
891         for (int i = 0; i < dumpedProcessCount; i++) {
892             Stats st = mWorkingProcs.get(i);
893             printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": "  "),
894                     st.pid, st.name, (int)st.rel_uptime,
895                     st.rel_utime, st.rel_stime, 0, 0, 0, st.rel_minfaults, st.rel_majfaults);
896             if (!st.removed && st.workingThreads != null) {
897                 int M = st.workingThreads.size();
898                 for (int j=0; j<M; j++) {
899                     Stats tst = st.workingThreads.get(j);
900                     printProcessCPU(pw,
901                             tst.added ? "   +" : (tst.removed ? "   -": "    "),
902                             tst.pid, tst.name, (int)st.rel_uptime,
903                             tst.rel_utime, tst.rel_stime, 0, 0, 0, 0, 0);
904                 }
905             }
906         }
907 
908         printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime,
909                 mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0);
910 
911         pw.flush();
912         return sw.toString();
913     }
914 
915     private void printRatio(PrintWriter pw, long numerator, long denominator) {
916         long thousands = (numerator*1000)/denominator;
917         long hundreds = thousands/10;
918         pw.print(hundreds);
919         if (hundreds < 10) {
920             long remainder = thousands - (hundreds*10);
921             if (remainder != 0) {
922                 pw.print('.');
923                 pw.print(remainder);
924             }
925         }
926     }
927 
928     private void printProcessCPU(PrintWriter pw, String prefix, int pid, String label,
929             int totalTime, int user, int system, int iowait, int irq, int softIrq,
930             int minFaults, int majFaults) {
931         pw.print(prefix);
932         if (totalTime == 0) totalTime = 1;
933         printRatio(pw, user+system+iowait+irq+softIrq, totalTime);
934         pw.print("% ");
935         if (pid >= 0) {
936             pw.print(pid);
937             pw.print("/");
938         }
939         pw.print(label);
940         pw.print(": ");
941         printRatio(pw, user, totalTime);
942         pw.print("% user + ");
943         printRatio(pw, system, totalTime);
944         pw.print("% kernel");
945         if (iowait > 0) {
946             pw.print(" + ");
947             printRatio(pw, iowait, totalTime);
948             pw.print("% iowait");
949         }
950         if (irq > 0) {
951             pw.print(" + ");
952             printRatio(pw, irq, totalTime);
953             pw.print("% irq");
954         }
955         if (softIrq > 0) {
956             pw.print(" + ");
957             printRatio(pw, softIrq, totalTime);
958             pw.print("% softirq");
959         }
960         if (minFaults > 0 || majFaults > 0) {
961             pw.print(" / faults:");
962             if (minFaults > 0) {
963                 pw.print(" ");
964                 pw.print(minFaults);
965                 pw.print(" minor");
966             }
967             if (majFaults > 0) {
968                 pw.print(" ");
969                 pw.print(majFaults);
970                 pw.print(" major");
971             }
972         }
973         pw.println();
974     }
975 
976     private void getName(Stats st, String cmdlineFile) {
977         String newName = st.name;
978         if (st.name == null
979                 || st.name.equals("app_process")
980                 || st.name.equals("<pre-initialized>")
981                 || st.name.equals("usap32")
982                 || st.name.equals("usap64")) {
983             String cmdName = ProcStatsUtil.readTerminatedProcFile(cmdlineFile, (byte) '\0');
984             if (cmdName != null && cmdName.length() > 1) {
985                 newName = cmdName;
986                 int i = newName.lastIndexOf("/");
987                 if (i > 0 && i < newName.length()-1) {
988                     newName = newName.substring(i+1);
989                 }
990             }
991             if (newName == null) {
992                 newName = st.baseName;
993             }
994         }
995         if (st.name == null || !newName.equals(st.name)) {
996             st.name = newName;
997             st.nameWidth = onMeasureProcessName(st.name);
998         }
999     }
1000 }
1001