ProcStatsData.java revision 30bbd902dd98197fd39b4e43bf8cb5027c49984b
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            for (int j = entry.mEntries.size() - 1; j >= 0; j--) {
205                ProcStatsEntry proc = entry.mEntries.get(j);
206                pkgRunTime += proc.mRunDuration;
207            }
208            long pkgZRam = (zramMem*pkgRunTime)/totalTime;
209            if (pkgZRam > 0) {
210                zramMem -= pkgZRam;
211                totalTime -= pkgRunTime;
212                ProcStatsEntry procEntry = new ProcStatsEntry(entry.mPackage, 0,
213                        mContext.getString(R.string.process_stats_os_zram), memTotalTime,
214                        pkgZRam);
215                procEntry.evaluateTargetPackage(mPm, mStats, null, null, sEntryCompare, mUseUss);
216                entry.addEntry(procEntry);
217            }
218        }
219    }
220
221    private ProcStatsPackageEntry createOsEntry(ProcessDataCollection bgTotals,
222            ProcessDataCollection runTotals, TotalMemoryUseCollection totalMem, long baseCacheRam) {
223        // Add in fake entry representing the OS itself.
224        ProcStatsPackageEntry osPkg = new ProcStatsPackageEntry("os", memTotalTime);
225        ProcStatsEntry osEntry;
226        if (totalMem.sysMemNativeWeight > 0) {
227            osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
228                    mContext.getString(R.string.process_stats_os_native), memTotalTime,
229                    (long) (totalMem.sysMemNativeWeight / memTotalTime));
230            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
231            osPkg.addEntry(osEntry);
232        }
233        if (totalMem.sysMemKernelWeight > 0) {
234            osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
235                    mContext.getString(R.string.process_stats_os_kernel), memTotalTime,
236                    (long) (totalMem.sysMemKernelWeight / memTotalTime));
237            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
238            osPkg.addEntry(osEntry);
239        }
240        /*  Turned off now -- zram is being distributed across running apps.
241        if (totalMem.sysMemZRamWeight > 0) {
242            osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
243                    mContext.getString(R.string.process_stats_os_zram), memTotalTime,
244                    (long) (totalMem.sysMemZRamWeight / memTotalTime));
245            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
246            osPkg.addEntry(osEntry);
247        }
248        */
249        if (baseCacheRam > 0) {
250            osEntry = new ProcStatsEntry(Utils.OS_PKG, 0,
251                    mContext.getString(R.string.process_stats_os_cache), memTotalTime,
252                    baseCacheRam / 1024);
253            osEntry.evaluateTargetPackage(mPm, mStats, bgTotals, runTotals, sEntryCompare, mUseUss);
254            osPkg.addEntry(osEntry);
255        }
256        return osPkg;
257    }
258
259    private ArrayList<ProcStatsEntry> getProcs(ProcessDataCollection bgTotals,
260            ProcessDataCollection runTotals) {
261        final ArrayList<ProcStatsEntry> procEntries = new ArrayList<>();
262        if (DEBUG) Log.d(TAG, "-------------------- PULLING PROCESSES");
263
264        final ProcessMap<ProcStatsEntry> entriesMap = new ProcessMap<ProcStatsEntry>();
265        for (int ipkg = 0, N = mStats.mPackages.getMap().size(); ipkg < N; ipkg++) {
266            final SparseArray<SparseArray<ProcessStats.PackageState>> pkgUids = mStats.mPackages
267                    .getMap().valueAt(ipkg);
268            for (int iu = 0; iu < pkgUids.size(); iu++) {
269                final SparseArray<ProcessStats.PackageState> vpkgs = pkgUids.valueAt(iu);
270                for (int iv = 0; iv < vpkgs.size(); iv++) {
271                    final ProcessStats.PackageState st = vpkgs.valueAt(iv);
272                    for (int iproc = 0; iproc < st.mProcesses.size(); iproc++) {
273                        final ProcessStats.ProcessState pkgProc = st.mProcesses.valueAt(iproc);
274                        final ProcessStats.ProcessState proc = mStats.mProcesses.get(pkgProc.mName,
275                                pkgProc.mUid);
276                        if (proc == null) {
277                            Log.w(TAG, "No process found for pkg " + st.mPackageName
278                                    + "/" + st.mUid + " proc name " + pkgProc.mName);
279                            continue;
280                        }
281                        ProcStatsEntry ent = entriesMap.get(proc.mName, proc.mUid);
282                        if (ent == null) {
283                            ent = new ProcStatsEntry(proc, st.mPackageName, bgTotals, runTotals,
284                                    mUseUss);
285                            if (ent.mRunWeight > 0) {
286                                if (DEBUG) Log.d(TAG, "Adding proc " + proc.mName + "/"
287                                            + proc.mUid + ": time="
288                                            + ProcessStatsUi.makeDuration(ent.mRunDuration) + " ("
289                                            + ((((double) ent.mRunDuration) / memTotalTime) * 100)
290                                            + "%)"
291                                            + " pss=" + ent.mAvgRunMem);
292                                entriesMap.put(proc.mName, proc.mUid, ent);
293                                procEntries.add(ent);
294                            }
295                        } else {
296                            ent.addPackage(st.mPackageName);
297                        }
298                    }
299                }
300            }
301        }
302
303        if (DEBUG) Log.d(TAG, "-------------------- MAPPING SERVICES");
304
305        // Add in service info.
306        for (int ip = 0, N = mStats.mPackages.getMap().size(); ip < N; ip++) {
307            SparseArray<SparseArray<ProcessStats.PackageState>> uids = mStats.mPackages.getMap()
308                    .valueAt(ip);
309            for (int iu = 0; iu < uids.size(); iu++) {
310                SparseArray<ProcessStats.PackageState> vpkgs = uids.valueAt(iu);
311                for (int iv = 0; iv < vpkgs.size(); iv++) {
312                    ProcessStats.PackageState ps = vpkgs.valueAt(iv);
313                    for (int is = 0, NS = ps.mServices.size(); is < NS; is++) {
314                        ProcessStats.ServiceState ss = ps.mServices.valueAt(is);
315                        if (ss.mProcessName != null) {
316                            ProcStatsEntry ent = entriesMap.get(ss.mProcessName,
317                                    uids.keyAt(iu));
318                            if (ent != null) {
319                                if (DEBUG) Log.d(TAG, "Adding service " + ps.mPackageName
320                                            + "/" + ss.mName + "/" + uids.keyAt(iu) + " to proc "
321                                            + ss.mProcessName);
322                                ent.addService(ss);
323                            } else {
324                                Log.w(TAG, "No process " + ss.mProcessName + "/" + uids.keyAt(iu)
325                                        + " for service " + ss.mName);
326                            }
327                        }
328                    }
329                }
330            }
331        }
332
333        return procEntries;
334    }
335
336    private void load() {
337        try {
338            ParcelFileDescriptor pfd = mProcessStats.getStatsOverTime(mDuration);
339            mStats = new ProcessStats(false);
340            InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
341            mStats.read(is);
342            try {
343                is.close();
344            } catch (IOException e) {
345            }
346            if (mStats.mReadError != null) {
347                Log.w(TAG, "Failure reading process stats: " + mStats.mReadError);
348            }
349        } catch (RemoteException e) {
350            Log.e(TAG, "RemoteException:", e);
351        }
352    }
353
354    public static class MemInfo {
355        double realUsedRam;
356        double realFreeRam;
357        double realTotalRam;
358        long baseCacheRam;
359
360        double[] mMemStateWeights = new double[ProcessStats.STATE_COUNT];
361        double freeWeight;
362        double usedWeight;
363        double weightToRam;
364        double totalRam;
365        double totalScale;
366        long memTotalTime;
367
368        private MemInfo(Context context, ProcessStats.TotalMemoryUseCollection totalMem,
369                long memTotalTime) {
370            this.memTotalTime = memTotalTime;
371            calculateWeightInfo(context, totalMem, memTotalTime);
372
373            double usedRam = (usedWeight * 1024) / memTotalTime;
374            double freeRam = (freeWeight * 1024) / memTotalTime;
375            totalRam = usedRam + freeRam;
376            totalScale = realTotalRam / totalRam;
377            weightToRam = totalScale / memTotalTime * 1024;
378
379            realUsedRam = usedRam * totalScale;
380            realFreeRam = freeRam * totalScale;
381            if (DEBUG) {
382                Log.i(TAG, "Scaled Used RAM: " + Formatter.formatShortFileSize(context,
383                        (long) realUsedRam));
384                Log.i(TAG, "Scaled Free RAM: " + Formatter.formatShortFileSize(context,
385                        (long) realFreeRam));
386            }
387            if (DEBUG) {
388                Log.i(TAG, "Adj Scaled Used RAM: " + Formatter.formatShortFileSize(context,
389                        (long) realUsedRam));
390                Log.i(TAG, "Adj Scaled Free RAM: " + Formatter.formatShortFileSize(context,
391                        (long) realFreeRam));
392            }
393
394            ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
395            ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(
396                    memInfo);
397            if (memInfo.hiddenAppThreshold >= realFreeRam) {
398                realUsedRam = freeRam;
399                realFreeRam = 0;
400                baseCacheRam = (long) realFreeRam;
401            } else {
402                realUsedRam += memInfo.hiddenAppThreshold;
403                realFreeRam -= memInfo.hiddenAppThreshold;
404                baseCacheRam = memInfo.hiddenAppThreshold;
405            }
406        }
407
408        private void calculateWeightInfo(Context context, TotalMemoryUseCollection totalMem,
409                long memTotalTime) {
410            MemInfoReader memReader = new MemInfoReader();
411            memReader.readMemInfo();
412            realTotalRam = memReader.getTotalSize();
413            freeWeight = totalMem.sysMemFreeWeight + totalMem.sysMemCachedWeight;
414            usedWeight = totalMem.sysMemKernelWeight + totalMem.sysMemNativeWeight
415                    + totalMem.sysMemZRamWeight;
416            for (int i = 0; i < ProcessStats.STATE_COUNT; i++) {
417                if (i == ProcessStats.STATE_SERVICE_RESTARTING) {
418                    // These don't really run.
419                    mMemStateWeights[i] = 0;
420                } else {
421                    mMemStateWeights[i] = totalMem.processStateWeight[i];
422                    if (i >= ProcessStats.STATE_HOME) {
423                        freeWeight += totalMem.processStateWeight[i];
424                    } else {
425                        usedWeight += totalMem.processStateWeight[i];
426                    }
427                }
428            }
429            if (DEBUG) {
430                Log.i(TAG, "Used RAM: " + Formatter.formatShortFileSize(context,
431                        (long) ((usedWeight * 1024) / memTotalTime)));
432                Log.i(TAG, "Free RAM: " + Formatter.formatShortFileSize(context,
433                        (long) ((freeWeight * 1024) / memTotalTime)));
434                Log.i(TAG, "Total RAM: " + Formatter.formatShortFileSize(context,
435                        (long) (((freeWeight + usedWeight) * 1024) / memTotalTime)));
436            }
437        }
438    }
439
440    final static Comparator<ProcStatsEntry> sEntryCompare = new Comparator<ProcStatsEntry>() {
441        @Override
442        public int compare(ProcStatsEntry lhs, ProcStatsEntry rhs) {
443            if (lhs.mRunWeight < rhs.mRunWeight) {
444                return 1;
445            } else if (lhs.mRunWeight > rhs.mRunWeight) {
446                return -1;
447            } else if (lhs.mRunDuration < rhs.mRunDuration) {
448                return 1;
449            } else if (lhs.mRunDuration > rhs.mRunDuration) {
450                return -1;
451            }
452            return 0;
453        }
454    };
455}
456