平台
RK3288 + Android 7.1
概述
查看系统内存使用情况, 如下图所示:
流程解读
一些字符显示
1.
2.
packages/apps/Settings/AndroidManifest.xml [入口]
<activity android:name="Settings$MemorySettingsActivity" android:label="@string/memory_settings_title" android:icon="@drawable/ic_settings_memory" android:taskAffinity=""> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="com.android.settings.SHORTCUT" /> </intent-filter> <intent-filter android:priority="3"> <action android:name="com.android.settings.action.SETTINGS" /> </intent-filter> <meta-data android:name="com.android.settings.category" android:value="com.android.settings.category.device" /> <meta-data android:name="com.android.settings.FRAGMENT_CLASS" android:value="com.android.settings.applications.ProcessStatsSummary" /> </activity>
相关源码
packages/apps/Settings/src/com/android/settings/applications/ProcessStatsSummary.java
packages/apps/Settings/src/com/android/settings/applications/ProcessStatsBase.java
packages/apps/Settings/src/com/android/settings/applications/ProcStatsData.java
packages/apps/Settings/src/com/android/settings/applications/ProcStatsPackageEntry.java
packages/apps/Settings/src/com/android/settings/applications/ProcessStatsUi.java
frameworks/base/services/core/java/com/android/server/am/ProcessStatsService.java
frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java
frameworks/base/services/core/java/com/android/server/am/ProcessStatsService.java
public ParcelFileDescriptor getStatsOverTime(long minTime) { mAm.mContext.enforceCallingOrSelfPermission( android.Manifest.permission.PACKAGE_USAGE_STATS, null); Parcel current = Parcel.obtain(); long curTime; synchronized (mAm) { long now = SystemClock.uptimeMillis(); mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); mProcessStats.mTimePeriodEndUptime = now; mProcessStats.writeToParcel(current, now, 0); curTime = mProcessStats.mTimePeriodEndRealtime - mProcessStats.mTimePeriodStartRealtime; } mWriteLock.lock(); try { if (curTime < minTime) { // Need to add in older stats to reach desired time. ArrayList<String> files = getCommittedFiles(0, false, true); if (files != null && files.size() > 0) { current.setDataPosition(0); ProcessStats stats = ProcessStats.CREATOR.createFromParcel(current); current.recycle(); int i = files.size()-1; while (i >= 0 && (stats.mTimePeriodEndRealtime - stats.mTimePeriodStartRealtime) < minTime) { AtomicFile file = new AtomicFile(new File(files.get(i))); i--; ProcessStats moreStats = new ProcessStats(false); readLocked(moreStats, file); if (moreStats.mReadError == null) { stats.add(moreStats); StringBuilder sb = new StringBuilder(); sb.append("Added stats: "); sb.append(moreStats.mTimePeriodStartClockStr); sb.append(", over "); TimeUtils.formatDuration(moreStats.mTimePeriodEndRealtime - moreStats.mTimePeriodStartRealtime, sb); Slog.i(TAG, sb.toString()); } else { Slog.w(TAG, "Failure reading " + files.get(i+1) + "; " + moreStats.mReadError); continue; } } current = Parcel.obtain(); stats.writeToParcel(current, 0); } } final byte[] outData = current.marshall(); current.recycle(); final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe(); Thread thr = new Thread("ProcessStats pipe output") { public void run() { FileOutputStream fout = new ParcelFileDescriptor.AutoCloseOutputStream(fds[1]); try { fout.write(outData); fout.close(); } catch (IOException e) { Slog.w(TAG, "Failure writing pipe", e); } } }; thr.start(); return fds[0]; } catch (IOException e) { Slog.w(TAG, "Failed building output pipe", e); } finally { mWriteLock.unlock(); } return null; } private ArrayList<String> getCommittedFiles(int minNum, boolean inclCurrent, boolean inclCheckedIn) { File[] files = mBaseDir.listFiles(); if (files == null || files.length <= minNum) { return null; } ArrayList<String> filesArray = new ArrayList<String>(files.length); String currentFile = mFile.getBaseFile().getPath(); if (DEBUG) Slog.d(TAG, "Collecting " + files.length + " files except: " + currentFile); for (int i=0; i<files.length; i++) { File file = files[i]; String fileStr = file.getPath(); if (DEBUG) Slog.d(TAG, "Collecting: " + fileStr); if (!inclCheckedIn && fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX)) { if (DEBUG) Slog.d(TAG, "Skipping: already checked in"); continue; } if (!inclCurrent && fileStr.equals(currentFile)) { if (DEBUG) Slog.d(TAG, "Skipping: current stats"); continue; } filesArray.add(fileStr); } Collections.sort(filesArray); return filesArray; }
mBaseDir 是由ActivityManagerService 创建ProcessStatsService时传进来的:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
// Note: This method is invoked on the main thread but may need to attach various // handlers to other threads. So take care to be explicit about the looper. public ActivityManagerService(Context systemContext) { //....... // TODO: Move creation of battery stats service outside of activity manager service. File dataDir = Environment.getDataDirectory(); File systemDir = new File(dataDir, "system"); mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats")); }
最终可以看到, ProcessStatsService会去读取data/system/procstats目录下的文件, 并解析传递数据
rk3288:/data/system/procstats # ll total 160 -rw------- 1 system system 11276 2013-01-19 13:45 state-2013-01-19-10-44-19.bin -rw------- 1 system system 7684 2013-01-19 16:47 state-2013-01-19-13-45-00.bin -rw------- 1 system system 7508 2013-01-19 19:49 state-2013-01-19-16-47-00.bin -rw------- 1 system system 7508 2013-01-19 22:50 state-2013-01-19-19-49-00.bin -rw------- 1 system system 7912 2013-01-20 01:52 state-2013-01-19-22-50-00.bin -rw------- 1 system system 7896 2013-01-20 04:54 state-2013-01-20-01-52-00.bin -rw------- 1 system system 8280 2013-01-20 07:56 state-2013-01-20-04-54-00.bin -rw------- 1 system system 7672 2013-01-20 10:58 state-2013-01-20-07-56-00.bin -rw------- 1 system system 7656 2013-01-20 11:58 state-2013-01-20-10-58-00.bin
procstats文件的读取:
static byte[] readFully(InputStream stream, int[] outLen) throws IOException { int pos = 0; final int initialAvail = stream.available(); byte[] data = new byte[initialAvail > 0 ? (initialAvail+1) : 16384]; while (true) { int amt = stream.read(data, pos, data.length-pos); if (DEBUG_PARCEL) Slog.i("foo", "Read " + amt + " bytes at " + pos + " of avail " + data.length); if (amt < 0) { if (DEBUG_PARCEL) Slog.i("foo", "**** FINISHED READING: pos=" + pos + " len=" + data.length); outLen[0] = pos; return data; } pos += amt; if (pos >= data.length) { byte[] newData = new byte[pos+16384]; if (DEBUG_PARCEL) Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length); System.arraycopy(data, 0, newData, 0, pos); data = newData; } } } public void read(InputStream stream) { try { int[] len = new int[1]; byte[] raw = readFully(stream, len); Parcel in = Parcel.obtain(); in.unmarshall(raw, 0, len[0]); in.setDataPosition(0); stream.close(); readFromParcel(in); } catch (IOException e) { mReadError = "caught exception: " + e; } }
扩展
dumpsys procstats详解
查看 RAM 使用情况数据 (procstats)
使用内存性能分析器查看应用的内存使用情况