ProcStatsData.java revision dc252eb7b1169dbdde6b6bf8821eb6f8babd4d26
1/*
2 * Copyright (C) 2015 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
17package com.android.settings.applications;
18
19import android.app.ActivityManager;
20import android.content.Context;
21import android.content.pm.PackageManager;
22import android.os.ParcelFileDescriptor;
23import android.os.RemoteException;
24import android.os.ServiceManager;
25import android.os.SystemClock;
26import android.text.format.Formatter;
27import android.util.ArrayMap;
28import android.util.Log;
29import android.util.SparseArray;
30
31import com.android.internal.app.IProcessStats;
32import com.android.internal.app.ProcessMap;
33import com.android.internal.app.ProcessStats;
34import com.android.internal.app.ProcessStats.ProcessDataCollection;
35import com.android.internal.app.ProcessStats.TotalMemoryUseCollection;
36import com.android.internal.util.MemInfoReader;
37import com.android.settings.R;
38import com.android.settings.Utils;
39
40import java.io.IOException;
41import java.io.InputStream;
42import java.util.ArrayList;
43import java.util.Comparator;
44import java.util.List;
45
46public class ProcStatsData {
47
48    private static final String TAG = "ProcStatsManager";
49
50    private static final boolean DEBUG = ProcessStatsUi.DEBUG;
51
52    private static ProcessStats sStatsXfer;
53
54    private PackageManager mPm;
55    private Context mContext;
56    private long memTotalTime;
57
58    private IProcessStats mProcessStats;
59    private ProcessStats mStats;
60
61    private boolean mUseUss;
62    private long mDuration;
63
64    private int[] mMemStates;
65
66    private int[] mStates;
67
68    private MemInfo mMemInfo;
69
70    private ArrayList<ProcStatsPackageEntry> pkgEntries;
71
72    public ProcStatsData(Context context, boolean useXfer) {
73        mContext = context;
74        mPm = context.getPackageManager();
75        mProcessStats = IProcessStats.Stub.asInterface(
76                ServiceManager.getService(ProcessStats.SERVICE_NAME));
77        mMemStates = ProcessStats.ALL_MEM_ADJ;
78        mStates = ProcessStats.BACKGROUND_PROC_STATES;
79        if (useXfer) {
80            mStats = sStatsXfer;
81        }
82    }
83
84    public void setTotalTime(int totalTime) {
85        memTotalTime = totalTime;
86    }
87
88    public void xferStats() {
89        sStatsXfer = mStats;
90    }
91
92    public void setMemStates(int[] memStates) {
93        mMemStates = memStates;
94        refreshStats(false);
95    }
96
97    public void setStats(int[] stats) {
98        this.mStates = stats;
99        refreshStats(false);
100    }
101
102    public int getMemState() {
103        int factor = mStats.mMemFactor;
104        if (factor == ProcessStats.ADJ_NOTHING) {
105            return ProcessStats.ADJ_MEM_FACTOR_NORMAL;
106        }
107        if (factor >= ProcessStats.ADJ_SCREEN_ON) {
108            factor -= ProcessStats.ADJ_SCREEN_ON;
109        }
110        return factor;
111    }
112
113    public MemInfo getMemInfo() {
114        return mMemInfo;
115    }
116
117    public long getElapsedTime() {
118        return mStats.mTimePeriodEndRealtime - mStats.mTimePeriodStartRealtime;
119    }
120
121    public void setDuration(long duration) {
122        if (duration != mDuration) {
123            mDuration = duration;
124            refreshStats(true);
125        }
126    }
127
128    public long getDuration() {
129        return mDuration;
130    }
131
132    public List<ProcStatsPackageEntry> getEntries() {
133        return pkgEntries;
134    }
135
136    public void refreshStats(boolean forceLoad) {
137        if (mStats == null || forceLoad) {
138            load();
139        }
140
141        pkgEntries = new ArrayList<>();
142
143        long now = SystemClock.uptimeMillis();
144
145        memTotalTime = ProcessStats.dumpSingleTime(null, null, mStats.mMemFactorDurations,
146                mStats.mMemFactor, mStats.mStartTime, now);
147
148        ProcessStats.TotalMemoryUseCollection totalMem = new ProcessStats.TotalMemoryUseCollection(
149                ProcessStats.ALL_SCREEN_ADJ, mMemStates);
150        mStats.computeTotalMemoryUse(totalMem, now);
151
152        mMemInfo = new MemInfo(mContext, totalMem, memTotalTime);
153
154        ProcessDataCollection bgTotals = new ProcessDataCollection(
155                ProcessStats.ALL_SCREEN_ADJ, mMemStates, mStates);
156        ProcessDataCollection runTotals = new ProcessDataCollection(
157                ProcessStats.ALL_SCREEN_ADJ, mMemStates, ProcessStats.NON_CACHED_PROC_STATES);
158
159        createPkgMap(getProcs(bgTotals, runTotals), bgTotals, runTotals);
160        if (totalMem.sysMemZRamWeight > 0) {
161            distributeZRam(totalMem.sysMemZRamWeight);
162        }
163
164        ProcStatsPackageEntry osPkg = createOsEntry(bgTotals, runTotals, totalMem,
165                mMemInfo.baseCacheRam);
166        pkgEntries.add(osPkg);
167    }
168
169    private void createPkgMap(ArrayList<ProcStatsEntry> procEntries, ProcessDataCollection bgTotals,
170            ProcessDataCollection runTotals) {
171        // Combine processes into packages.
172        ArrayMap<String, ProcStatsPackageEntry> pkgMap = new ArrayMap<>();
173        for (int i = procEntries.size() - 1; i >= 0; i--) {
174            ProcStatsEntry proc = procEntries.get(i);
175            proc.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
176            ProcStatsPackageEntry pkg = pkgMap.get(proc.mBestTargetPackage);
177            if (pkg == null) {
178                pkg = new ProcStatsPackageEntry(proc.mBestTargetPackage, memTotalTime);
179                pkgMap.put(proc.mBestTargetPackage, pkg);
180                pkgEntries.add(pkg);
181            }
182            pkg.addEntry(proc);
183        }
184    }
185
186    private void distributeZRam(double zramWeight) {
187        // Distribute kernel's Z-Ram across processes, based on how much they have been running.
188        // The idea is that the memory used by the kernel for this is not really the kernel's
189        // responsibility, but that of whoever got swapped in to it...  and we will take how
190        // much a process runs for as a sign of the proportion of Z-Ram it is responsible for.
191
192        long zramMem = (long) (zramWeight / memTotalTime);
193        long totalTime = 0;
194        for (int i = pkgEntries.size() - 1; i >= 0; i--) {
195            ProcStatsPackageEntry entry = pkgEntries.get(i);
196            for (int j = entry.mEntries.size() - 1; j >= 0; j--) {
197                ProcStatsEntry proc = entry.mEntries.get(j);
198                totalTime += proc.mRunDuration;
199            }
200        }
201        for (int i = pkgEntries.size() - 1; i >= 0 && totalTime > 0; i--) {
202            ProcStatsPackageEntry entry = pkgEntries.get(i);
203            long pkgRunTime = 0;
204            long maxRunTime = 0;
205            for (int j = entry.mEntries.size() - 1; j >= 0; j--) {
206                ProcStatsEntry proc = entry.mEntries.get(j);
207                pkgRunTime += proc.mRunDuration;
208                if (proc.mRunDuration > maxRunTime) {
209                    maxRunTime = proc.mRunDuration;
210                }
211            }
212            long pkgZRam = (zramMem*pkgRunTime)/totalTime;
213            if (pkgZRam > 0) {
214                zramMem -= pkgZRam;
215                totalTime -= pkgRunTime;
216                ProcStatsEntry procEntry = new ProcStatsEntry(entry.mPackage, 0,
217                        mContext.getString(R.string.process_stats_os_zram), maxRunTime,
218                        pkgZRam, memTotalTime);
219                procEntry.evaluateTargetPackage(mPm, mStats, null, null, sEntryCompare, mUseUss);
220                entry.addEntry(procEntry);
221            }
222        }
223    }
224
225    private ProcStatsPackageEntry createOsEntry(ProcessDataCollection bgTotals,
226            ProcessDataCollection runTotals, TotalMemoryUseCollection totalMem, long baseCacheRam) {
227        // Add in fake entry representing the OS itself.
228        ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime);
229        ProcStatsEntry osEntry;
230        if (totalMem.sysMemNativeWeight > 0) {
231            osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
232                    mContext.getString(R.string.process_stats_os_native), memTotalTime,
233                    (long) (totalMem.sysMemNativeWeight / memTotalTime), memTotalTime);
234            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
235            osPkg.addEntry(osEntry);
236        }
237        if (totalMem.sysMemKernelWeight > 0) {
238            osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
239                    mContext.getString(R.string.process_stats_os_kernel), memTotalTime,
240                    (long) (totalMem.sysMemKernelWeight / memTotalTime), memTotalTime);
241            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
242            osPkg.addEntry(osEntry);
243        }
244        /*  Turned off now -- zram is being distributed across running apps.
245        if (totalMem.sysMemZRamWeight > 0) {
246            osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
247                    mContext.getString(R.string.process_stats_os_zram), memTotalTime,
248                    (long) (totalMem.sysMemZRamWeight / memTotalTime));
249            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
250            osPkg.addEntry(osEntry);
251        }
252        */
253        if (baseCacheRam > 0) {
254            osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
255                    mContext.getString(R.string.process_stats_os_cache), memTotalTime,
256                    baseCacheRam / 1024, memTotalTime);
257            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
258            osPkg.addEntry(osEntry);
259        }
260        return osPkg;
261    }
262
263    private ArrayList<ProcStatsEntry> getProcs(ProcessDataCollection bgTotals,
264            ProcessDataCollection runTotals) {
265        final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>();
266        if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES");
267
268        final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>();
269        for (int ipkg = 0, N = mStats.mPackages.getMap().size(); ipkg < N; ipkg++) {
270            final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids = mStats.mPackages
271                    .getMap().valueAt(ipkg);
272            for (int iu = 0; iu < pkgUids.size(); iu++) {
273                final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu);
274                for (int iv = 0; iv < vpkgs.size(); iv++) {
275                    final ProcessStats.PackageState st = vpkgs.valueAt(iv);
276                    for (int iproc = 0; iproc < st.mProcesses.size(); iproc++) {
277                        final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc);
278                        final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName,
279                                pkgProc.mUid);
280                        if (proc == null) {
281                            Log.w(TAG, "No process found for pkg " + st.mPackageName
282                                    + "/" + st.mUid + " proc name " + pkgProc.mName);
283                            continue;
284                        }
285                        ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid);
286                        if (ent == null) {
287                            ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals,
288                                    mUseUss);
289                            if (ent.mRunWeight > 0) {
290                                if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/"
291                                            + proc.mUid + ": time="
292                                            + ProcessStatsUi.makeDuration(ent.mRunDuration) + " ("
293                                            + ((((double) ent.mRunDuration) / memTotalTime) * 100)
294                                            + "%)"
295                                            + " pss=" + ent.mAvgRunMem);
296                                entriesMap.put(proc.mName, proc.mUid, ent);
297                                procEntries.add(ent);
298                            }
299                        } else {
300                            ent.addPackage(st.mPackageName);
301                        }
302                    }
303                }
304            }
305        }
306
307        if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES");
308
309        // Add in service info.
310        for (int ip = 0, N = mStats.mPackages.getMap().size(); ip < N; ip++) {
311            SparseArray<SparseArray<ProcessStats.PackageState>> uids = mStats.mPackages.getMap()
312                    .valueAt(ip);
313            for (int iu = 0; iu < uids.size(); iu++) {
314                SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu);
315                for (int iv = 0; iv < vpkgs.size(); iv++) {
316                    ProcessStats.PackageState ps = vpkgs.valueAt(iv);
317                    for (int is = 0, NS = ps.mServices.size(); is < NS; is++) {
318                        ProcessStats.ServiceState ss = ps.mServices.valueAt(is);
319                        if (ss.mProcessName != null) {
320                            ProcStatsEntry ent = entriesMap.get(ss.mProcessName,
321                                    uids.keyAt(iu));
322                            if (ent != null) {
323                                if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName
324                                            + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc "
325                                            + ss.mProcessName);
326                                ent.addService(ss);
327                            } else {
328                                Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu)
329                                        + " for service " + ss.mName);
330                            }
331                        }
332                    }
333                }
334            }
335        }
336
337        return procEntries;
338    }
339
340    private void load() {
341        try {
342            ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration);
343            mStats = new ProcessStats(false);
344            InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
345            mStats.read(is);
346            try {
347                is.close();
348            } catch (IOException e) {
349            }
350            if (mStats.mReadError != null) {
351                Log.w(TAG, "Failure reading process stats: " + mStats.mReadError);
352            }
353        } catch (RemoteException e) {
354            Log.e(TAG, "RemoteException:", e);
355        }
356    }
357
358    public static class MemInfo {
359        public double realUsedRam;
360        public double realFreeRam;
361        public double realTotalRam;
362        long baseCacheRam;
363
364        double[] mMemStateWeights = new double[ProcessStats.STATE_COUNT];
365        double freeWeight;
366        double usedWeight;
367        double weightToRam;
368        double totalRam;
369        double totalScale;
370        long memTotalTime;
371
372        private MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem,
373                long memTotalTime) {
374            this.memTotalTime = memTotalTime;
375            calculateWeightInfo(context, totalMem, memTotalTime);
376
377            double usedRam = (usedWeight * 1024) / memTotalTime;
378            double freeRam = (freeWeight * 1024) / memTotalTime;
379            totalRam = usedRam + freeRam;
380            totalScale = realTotalRam / totalRam;
381            weightToRam = totalScale / memTotalTime * 1024;
382
383            realUsedRam = usedRam * totalScale;
384            realFreeRam = freeRam * totalScale;
385            if (DEBUG) {
386                Log.i(TAG, "Scaled Used RAM: " + Formatter.formatShortFileSize(context,
387                        (long) realUsedRam));
388                Log.i(TAG, "Scaled Free RAM: " + Formatter.formatShortFileSize(context,
389                        (long) realFreeRam));
390            }
391            if (DEBUG) {
392                Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(context,
393                        (long) realUsedRam));
394                Log.i(TAG, "Adj Scaled Free RAM: " + Formatter.formatShortFileSize(context,
395                        (long) realFreeRam));
396            }
397
398            ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
399            ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(
400                    memInfo);
401            if (memInfo.hiddenAppThreshold >= realFreeRam) {
402                realUsedRam = freeRam;
403                realFreeRam = 0;
404                baseCacheRam = (long) realFreeRam;
405            } else {
406                realUsedRam += memInfo.hiddenAppThreshold;
407                realFreeRam -= memInfo.hiddenAppThreshold;
408                baseCacheRam = memInfo.hiddenAppThreshold;
409            }
410        }
411
412        private void calculateWeightInfo(Context context, TotalMemoryUseCollection totalMem,
413                long memTotalTime) {
414            MemInfoReader memReader = new MemInfoReader();
415            memReader.readMemInfo();
416            realTotalRam = memReader.getTotalSize();
417            freeWeight = totalMem.sysMemFreeWeight + totalMem.sysMemCachedWeight;
418            usedWeight = totalMem.sysMemKernelWeight + totalMem.sysMemNativeWeight
419                    + totalMem.sysMemZRamWeight;
420            for (int i = 0; i < ProcessStats.STATE_COUNT; i++) {
421                if (i == ProcessStats.STATE_SERVICE_RESTARTING) {
422                    // These don't really run.
423                    mMemStateWeights[i] = 0;
424                } else {
425                    mMemStateWeights[i] = totalMem.processStateWeight[i];
426                    if (i >= ProcessStats.STATE_HOME) {
427                        freeWeight += totalMem.processStateWeight[i];
428                    } else {
429                        usedWeight += totalMem.processStateWeight[i];
430                    }
431                }
432            }
433            if (DEBUG) {
434                Log.i(TAG, "Used RAM: " + Formatter.formatShortFileSize(context,
435                        (long) ((usedWeight * 1024) / memTotalTime)));
436                Log.i(TAG, "Free RAM: " + Formatter.formatShortFileSize(context,
437                        (long) ((freeWeight * 1024) / memTotalTime)));
438                Log.i(TAG, "Total RAM: " + Formatter.formatShortFileSize(context,
439                        (long) (((freeWeight + usedWeight) * 1024) / memTotalTime)));
440            }
441        }
442    }
443
444    final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
445        @Override
446        public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
447            if (lhs.mRunWeight < rhs.mRunWeight) {
448                return 1;
449            } else if (lhs.mRunWeight > rhs.mRunWeight) {
450                return -1;
451            } else if (lhs.mRunDuration < rhs.mRunDuration) {
452                return 1;
453            } else if (lhs.mRunDuration > rhs.mRunDuration) {
454                return -1;
455            }
456            return 0;
457        }
458    };
459}
460