summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/util/proto/ProtoUtils.java39
-rw-r--r--core/java/com/android/internal/app/procstats/DumpUtils.java19
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessState.java81
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessStats.java42
-rw-r--r--core/proto/android/os/incident.proto6
-rw-r--r--core/proto/android/service/procstats.proto168
-rw-r--r--core/proto/android/util/common.proto33
-rw-r--r--services/core/java/com/android/server/am/ProcessStatsService.java51
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();
+ }
}