diff options
| -rw-r--r-- | core/java/android/util/proto/ProtoUtils.java | 39 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/procstats/DumpUtils.java | 19 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/procstats/ProcessState.java | 81 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/procstats/ProcessStats.java | 42 | ||||
| -rw-r--r-- | core/proto/android/os/incident.proto | 6 | ||||
| -rw-r--r-- | core/proto/android/service/procstats.proto | 168 | ||||
| -rw-r--r-- | core/proto/android/util/common.proto | 33 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ProcessStatsService.java | 51 |
8 files changed, 437 insertions, 2 deletions
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java new file mode 100644 index 000000000000..449baca5c844 --- /dev/null +++ b/core/java/android/util/proto/ProtoUtils.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util.proto; + +import android.util.AggStats; + +/** + * This class contains a list of helper functions to write common proto in + * //frameworks/base/core/proto/android/base directory + */ +public class ProtoUtils { + + /** + * Dump AggStats to ProtoOutputStream + * @hide + */ + public static void toAggStatsProto(ProtoOutputStream proto, long fieldId, + long min, long average, long max) { + final long aggStatsToken = proto.start(fieldId); + proto.write(AggStats.MIN, min); + proto.write(AggStats.AVERAGE, average); + proto.write(AggStats.MAX, max); + proto.end(aggStatsToken); + } +} diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java index ebedc89cab1e..0bc8c483313d 100644 --- a/core/java/com/android/internal/app/procstats/DumpUtils.java +++ b/core/java/com/android/internal/app/procstats/DumpUtils.java @@ -29,6 +29,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import static com.android.internal.app.procstats.ProcessStats.*; @@ -66,6 +67,8 @@ public final class DumpUtils { "cch-activity", "cch-aclient", "cch-empty" }; + // State enum is defined in frameworks/base/core/proto/android/service/procstats.proto + // Update states must sync enum definition as well, the ordering must not be changed. static final String[] ADJ_SCREEN_TAGS = new String[] { "0", "1" }; @@ -177,6 +180,13 @@ public final class DumpUtils { printArrayEntry(pw, STATE_TAGS, state, 1); } + public static void printProcStateTagProto(ProtoOutputStream proto, long screenId, long memId, + long stateId, int state) { + state = printProto(proto, screenId, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD * STATE_COUNT); + state = printProto(proto, memId, ADJ_MEM_TAGS, state, STATE_COUNT); + printProto(proto, stateId, STATE_TAGS, state, 1); + } + public static void printAdjTag(PrintWriter pw, int state) { state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD); printArrayEntry(pw, ADJ_MEM_TAGS, state, 1); @@ -352,6 +362,15 @@ public final class DumpUtils { return value - index*mod; } + public static int printProto(ProtoOutputStream proto, long fieldId, String[] array, int value, int mod) { + int index = value/mod; + if (index >= 0 && index < array.length) { + // Valid state enum number starts at 1, 0 stands for unknown. + proto.write(fieldId, index + 1); + } // else enum default is always zero in proto3 + return value - index*mod; + } + public static String collapseString(String pkgName, String itemName) { if (itemName.startsWith(pkgName)) { final int ITEMLEN = itemName.length(); diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java index e0a40536f255..42c051d432b9 100644 --- a/core/java/com/android/internal/app/procstats/ProcessState.java +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -21,6 +21,8 @@ import android.os.Parcelable; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.service.pm.PackageProto; +import android.service.procstats.ProcessStatsProto; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; @@ -29,6 +31,8 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; +import android.util.proto.ProtoUtils; import com.android.internal.app.procstats.ProcessStats; import com.android.internal.app.procstats.ProcessStats.PackageState; @@ -69,6 +73,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Objects; public final class ProcessState { @@ -1157,6 +1164,7 @@ public final class ProcessState { } } + @Override public String toString() { StringBuilder sb = new StringBuilder(128); sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this))) @@ -1167,4 +1175,77 @@ public final class ProcessState { sb.append("}"); return sb.toString(); } + + public void toProto(ProtoOutputStream proto, String procName, int uid, long now) { + proto.write(ProcessStatsProto.PROCESS, procName); + proto.write(ProcessStatsProto.UID, uid); + if (mNumExcessiveCpu > 0 || mNumCachedKill > 0 ) { + final long killToken = proto.start(ProcessStatsProto.KILL); + proto.write(ProcessStatsProto.Kill.WAKES, mNumExcessiveWake); + proto.write(ProcessStatsProto.Kill.CPU, mNumExcessiveCpu); + proto.write(ProcessStatsProto.Kill.CACHED, mNumCachedKill); + ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.Kill.CACHED_PSS, + mMinCachedKillPss, mAvgCachedKillPss, mMaxCachedKillPss); + proto.end(killToken); + } + + // Group proc stats by type (screen state + mem state + process state) + Map<Integer, Long> durationByState = new HashMap<>(); + boolean didCurState = false; + for (int i=0; i<mDurations.getKeyCount(); i++) { + final int key = mDurations.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + long time = mDurations.getValue(key); + if (mCurState == type) { + didCurState = true; + time += now - mStartTime; + } + durationByState.put(type, time); + } + if (!didCurState && mCurState != STATE_NOTHING) { + durationByState.put(mCurState, now - mStartTime); + } + + for (int i=0; i<mPssTable.getKeyCount(); i++) { + final int key = mPssTable.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + if (!durationByState.containsKey(type)) { + // state without duration should not have stats! + continue; + } + final long stateToken = proto.start(ProcessStatsProto.STATES); + DumpUtils.printProcStateTagProto(proto, + ProcessStatsProto.State.SCREEN_STATE, + ProcessStatsProto.State.MEMORY_STATE, + ProcessStatsProto.State.PROCESS_STATE, + type); + + long duration = durationByState.get(type); + durationByState.remove(type); // remove the key since it is already being dumped. + proto.write(ProcessStatsProto.State.DURATION_MS, duration); + + proto.write(ProcessStatsProto.State.SAMPLE_SIZE, mPssTable.getValue(key, PSS_SAMPLE_COUNT)); + ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.PSS, + mPssTable.getValue(key, PSS_MINIMUM), + mPssTable.getValue(key, PSS_AVERAGE), + mPssTable.getValue(key, PSS_MAXIMUM)); + ProtoUtils.toAggStatsProto(proto, ProcessStatsProto.State.USS, + mPssTable.getValue(key, PSS_USS_MINIMUM), + mPssTable.getValue(key, PSS_USS_AVERAGE), + mPssTable.getValue(key, PSS_USS_MAXIMUM)); + + proto.end(stateToken); + } + + for (Map.Entry<Integer, Long> entry : durationByState.entrySet()) { + final long stateToken = proto.start(ProcessStatsProto.STATES); + DumpUtils.printProcStateTagProto(proto, + ProcessStatsProto.State.SCREEN_STATE, + ProcessStatsProto.State.MEMORY_STATE, + ProcessStatsProto.State.PROCESS_STATE, + entry.getKey()); + proto.write(ProcessStatsProto.State.DURATION_MS, entry.getValue()); + proto.end(stateToken); + } + } } diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java index 35b53c2298bb..14f5e5b5a704 100644 --- a/core/java/com/android/internal/app/procstats/ProcessStats.java +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -22,6 +22,7 @@ import android.os.Parcelable; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.service.procstats.ProcessStatsSectionProto; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; @@ -30,6 +31,7 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.app.ProcessMap; import com.android.internal.app.procstats.DurationsTable; @@ -1706,6 +1708,46 @@ public final class ProcessStats implements Parcelable { } } + public void toProto(ProtoOutputStream proto, long now) { + final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + + proto.write(ProcessStatsSectionProto.START_REALTIME_MS, mTimePeriodStartRealtime); + proto.write(ProcessStatsSectionProto.END_REALTIME_MS, + mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); + proto.write(ProcessStatsSectionProto.START_UPTIME_MS, mTimePeriodStartUptime); + proto.write(ProcessStatsSectionProto.END_UPTIME_MS, mTimePeriodEndUptime); + proto.write(ProcessStatsSectionProto.RUNTIME, mRuntime); + proto.write(ProcessStatsSectionProto.HAS_SWAPPED_PSS, mHasSwappedOutPss); + boolean partial = true; + if ((mFlags&FLAG_SHUTDOWN) != 0) { + proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SHUTDOWN); + partial = false; + } + if ((mFlags&FLAG_SYSPROPS) != 0) { + proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_SYSPROPS); + partial = false; + } + if ((mFlags&FLAG_COMPLETE) != 0) { + proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_COMPLETE); + partial = false; + } + if (partial) { + proto.write(ProcessStatsSectionProto.STATUS, ProcessStatsSectionProto.STATUS_PARTIAL); + } + + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final ProcessState procState = uids.valueAt(iu); + final long processStateToken = proto.start(ProcessStatsSectionProto.PROCESS_STATS); + procState.toProto(proto, procName, uid, now); + proto.end(processStateToken); + } + } + } final public static class ProcessStateHolder { public final int appVersion; diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index e823561ebb73..c3ceb1ca7717 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -30,6 +30,7 @@ import "frameworks/base/core/proto/android/service/notification.proto"; import "frameworks/base/core/proto/android/service/package.proto"; import "frameworks/base/core/proto/android/service/power.proto"; import "frameworks/base/core/proto/android/service/print.proto"; +import "frameworks/base/core/proto/android/service/procstats.proto"; import "frameworks/base/core/proto/android/providers/settings.proto"; import "frameworks/base/core/proto/android/os/incidentheader.proto"; import "frameworks/base/core/proto/android/os/kernelwake.proto"; @@ -96,4 +97,9 @@ message IncidentProto { android.service.pm.PackageServiceDumpProto package = 3008; android.service.power.PowerServiceDumpProto power = 3009; android.service.print.PrintServiceDumpProto print = 3010; + + android.service.procstats.ProcessStatsServiceDumpProto procstats = 3011 [ + (section).type = SECTION_DUMPSYS, + (section).args = "procstats --proto" + ]; } diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto new file mode 100644 index 000000000000..885150e91f81 --- /dev/null +++ b/core/proto/android/service/procstats.proto @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +option java_multiple_files = true; +option java_outer_classname = "ProcessStatsServiceProto"; + +import "frameworks/base/core/proto/android/util/common.proto"; + +package android.service.procstats; + +/** + * Data from ProcStatsService Dumpsys + * + * Next Tag: 4 + */ +message ProcessStatsServiceDumpProto { + + ProcessStatsSectionProto procstats_now = 1; + + ProcessStatsSectionProto procstats_over_3hrs = 2; + + ProcessStatsSectionProto procstats_over_24hrs = 3; +} + +/** + * Data model from /frameworks/base/core/java/com/android/internal/app/procstats/ProcessStats.java + * This proto is defined based on the writeToParcel method. + * + * Next Tag: 9 + */ +message ProcessStatsSectionProto { + + // Elapsed realtime at start of report. + int64 start_realtime_ms = 1; + + // Elapsed realtime at end of report. + int64 end_realtime_ms = 2; + + // CPU uptime at start of report. + int64 start_uptime_ms = 3; + + // CPU uptime at end of report. + int64 end_uptime_ms = 4; + + // System runtime library. e.g. "libdvm.so", "libart.so". + string runtime = 5; + + // whether kernel reports swapped pss. + bool has_swapped_pss = 6; + + // Data completeness. e.g. "complete", "partial", shutdown", or "sysprops". + enum Status { + STATUS_UNKNOWN = 0; + STATUS_COMPLETE = 1; + STATUS_PARTIAL = 2; + STATUS_SHUTDOWN = 3; + STATUS_SYSPROPS = 4; + } + repeated Status status = 7; + + // Stats for each process. + repeated ProcessStatsProto process_stats = 8; +} + +// Next Tag: 6 +message ProcessStatsProto { + + // Name of process. + string process = 1; + + // Uid of the process. + int32 uid = 2; + + // Information about how often kills occurred + message Kill { + // Count of excessive wakes kills + int32 wakes = 1; + + // Count of excessive CPU kills + int32 cpu = 2; + + // Count of kills when cached + int32 cached = 3; + + // PSS stats during cached kill + android.util.AggStats cached_pss = 4; + } + Kill kill = 3; + + message State { + enum ScreenState { + SCREEN_UNKNOWN = 0; + OFF = 1; + ON = 2; + } + ScreenState screen_state = 1; + + enum MemoryState { + MEMORY_UNKNOWN = 0; + NORMAL = 1; // normal. + MODERATE = 2; // moderate memory pressure. + LOW = 3; // low memory. + CRITICAL = 4; // critical memory. + } + MemoryState memory_state = 2; + + enum ProcessState { + PROCESS_UNKNOWN = 0; + // Persistent system process. + PERSISTENT = 1; + // Top activity; actually any visible activity. + TOP = 2; + // Important foreground process (ime, wallpaper, etc). + IMPORTANT_FOREGROUND = 3; + // Important background process. + IMPORTANT_BACKGROUND = 4; + // Performing backup operation. + BACKUP = 5; + // Heavy-weight process (currently not used). + HEAVY_WEIGHT = 6; + // Background process running a service. + SERVICE = 7; + // Process not running, but would be if there was enough RAM. + SERVICE_RESTARTING = 8; + // Process running a receiver. + RECEIVER = 9; + // Process hosting home/launcher app when not on top. + HOME = 10; + // Process hosting the last app the user was in. + LAST_ACTIVITY = 11; + // Cached process hosting a previous activity. + CACHED_ACTIVITY = 12; + // Cached process hosting a client activity. + CACHED_ACTIVITY_CLIENT = 13; + // Cached process that is empty. + CACHED_EMPTY = 14; + } + ProcessState process_state = 3; + + // Millisecond duration spent in this state + int64 duration_ms = 4; + + // # of samples taken + int32 sample_size = 5; + + // PSS is memory reserved for this process + android.util.AggStats pss = 6; + + // USS is memory shared between processes, divided evenly for accounting + android.util.AggStats uss = 7; + } + repeated State states = 5; +} diff --git a/core/proto/android/util/common.proto b/core/proto/android/util/common.proto new file mode 100644 index 000000000000..6dd4c0205541 --- /dev/null +++ b/core/proto/android/util/common.proto @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto3"; + +package android.util; + +option java_multiple_files = true; + +/** + * Very basic data structure used by aggregated stats. + */ +message AggStats { + + int64 min = 1; + + int64 average = 2; + + int64 max = 3; +} diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index 39aed7cf3d67..effb86c1bc07 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -23,11 +23,14 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; +import android.service.procstats.ProcessStatsProto; +import android.service.procstats.ProcessStatsServiceDumpProto; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.DumpUtils; @@ -622,13 +625,17 @@ public final class ProcessStatsService extends IProcessStats.Stub { long ident = Binder.clearCallingIdentity(); try { - dumpInner(fd, pw, args); + if (args.length > 0 && "--proto".equals(args[0])) { + dumpProto(fd); + } else { + dumpInner(pw, args); + } } finally { Binder.restoreCallingIdentity(ident); } } - private void dumpInner(FileDescriptor fd, PrintWriter pw, String[] args) { + private void dumpInner(PrintWriter pw, String[] args) { final long now = SystemClock.uptimeMillis(); boolean isCheckin = false; @@ -1038,4 +1045,44 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } } + + private void dumpAggregatedStats(ProtoOutputStream proto, int aggregateHours, long now) { + ParcelFileDescriptor pfd = getStatsOverTime(aggregateHours*60*60*1000 + - (ProcessStats.COMMIT_PERIOD/2)); + if (pfd == null) { + return; + } + ProcessStats stats = new ProcessStats(false); + InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + stats.read(stream); + if (stats.mReadError != null) { + return; + } + stats.toProto(proto, now); + } + + private void dumpProto(FileDescriptor fd) { + final ProtoOutputStream proto = new ProtoOutputStream(fd); + + // dump current procstats + long nowToken = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_NOW); + long now; + synchronized (mAm) { + now = SystemClock.uptimeMillis(); + mProcessStats.toProto(proto, now); + } + proto.end(nowToken); + + // aggregated over last 3 hours procstats + long tokenOf3Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_3HRS); + dumpAggregatedStats(proto, 3, now); + proto.end(tokenOf3Hrs); + + // aggregated over last 24 hours procstats + long tokenOf24Hrs = proto.start(ProcessStatsServiceDumpProto.PROCSTATS_OVER_24HRS); + dumpAggregatedStats(proto, 24, now); + proto.end(tokenOf24Hrs); + + proto.flush(); + } } |