blob: eda04a6a322ac3af5c88be0ba5e466f8d59cd6a9 [file] [log] [blame]
/*
* Copyright (C) 2013 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 com.android.internal.app.procstats;
import android.os.UserHandle;
import android.service.procstats.ProcessStatsEnums;
import android.service.procstats.ProcessStatsStateProto;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import static com.android.internal.app.procstats.ProcessStats.*;
import java.io.PrintWriter;
import java.util.ArrayList;
/**
* Utilities for dumping.
*/
public final class DumpUtils {
public static final String[] STATE_NAMES;
public static final String[] STATE_LABELS;
public static final String STATE_LABEL_TOTAL;
public static final String STATE_LABEL_CACHED;
public static final String[] STATE_NAMES_CSV;
static final String[] STATE_TAGS;
static final int[] STATE_PROTO_ENUMS;
// Make the mapping easy to update.
static {
STATE_NAMES = new String[STATE_COUNT];
STATE_NAMES[STATE_PERSISTENT] = "Persist";
STATE_NAMES[STATE_TOP] = "Top";
STATE_NAMES[STATE_IMPORTANT_FOREGROUND] = "ImpFg";
STATE_NAMES[STATE_IMPORTANT_BACKGROUND] = "ImpBg";
STATE_NAMES[STATE_BACKUP] = "Backup";
STATE_NAMES[STATE_SERVICE] = "Service";
STATE_NAMES[STATE_SERVICE_RESTARTING] = "ServRst";
STATE_NAMES[STATE_RECEIVER] = "Receivr";
STATE_NAMES[STATE_HEAVY_WEIGHT] = "HeavyWt";
STATE_NAMES[STATE_HOME] = "Home";
STATE_NAMES[STATE_LAST_ACTIVITY] = "LastAct";
STATE_NAMES[STATE_CACHED_ACTIVITY] = "CchAct";
STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT] = "CchCAct";
STATE_NAMES[STATE_CACHED_EMPTY] = "CchEmty";
STATE_LABELS = new String[STATE_COUNT];
STATE_LABELS[STATE_PERSISTENT] = "Persistent";
STATE_LABELS[STATE_TOP] = " Top";
STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg";
STATE_LABELS[STATE_IMPORTANT_BACKGROUND] = " Imp Bg";
STATE_LABELS[STATE_BACKUP] = " Backup";
STATE_LABELS[STATE_SERVICE] = " Service";
STATE_LABELS[STATE_SERVICE_RESTARTING] = "Service Rs";
STATE_LABELS[STATE_RECEIVER] = " Receiver";
STATE_LABELS[STATE_HEAVY_WEIGHT] = " Heavy Wgt";
STATE_LABELS[STATE_HOME] = " (Home)";
STATE_LABELS[STATE_LAST_ACTIVITY] = "(Last Act)";
STATE_LABELS[STATE_CACHED_ACTIVITY] = " (Cch Act)";
STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT] = "(Cch CAct)";
STATE_LABELS[STATE_CACHED_EMPTY] = "(Cch Emty)";
STATE_LABEL_CACHED = " (Cached)";
STATE_LABEL_TOTAL = " TOTAL";
STATE_NAMES_CSV = new String[STATE_COUNT];
STATE_NAMES_CSV[STATE_PERSISTENT] = "pers";
STATE_NAMES_CSV[STATE_TOP] = "top";
STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND] = "impfg";
STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND] = "impbg";
STATE_NAMES_CSV[STATE_BACKUP] = "backup";
STATE_NAMES_CSV[STATE_SERVICE] = "service";
STATE_NAMES_CSV[STATE_SERVICE_RESTARTING] = "service-rs";
STATE_NAMES_CSV[STATE_RECEIVER] = "receiver";
STATE_NAMES_CSV[STATE_HEAVY_WEIGHT] = "heavy";
STATE_NAMES_CSV[STATE_HOME] = "home";
STATE_NAMES_CSV[STATE_LAST_ACTIVITY] = "lastact";
STATE_NAMES_CSV[STATE_CACHED_ACTIVITY] = "cch-activity";
STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT] = "cch-aclient";
STATE_NAMES_CSV[STATE_CACHED_EMPTY] = "cch-empty";
STATE_TAGS = new String[STATE_COUNT];
STATE_TAGS[STATE_PERSISTENT] = "p";
STATE_TAGS[STATE_TOP] = "t";
STATE_TAGS[STATE_IMPORTANT_FOREGROUND] = "f";
STATE_TAGS[STATE_IMPORTANT_BACKGROUND] = "b";
STATE_TAGS[STATE_BACKUP] = "u";
STATE_TAGS[STATE_SERVICE] = "s";
STATE_TAGS[STATE_SERVICE_RESTARTING] = "x";
STATE_TAGS[STATE_RECEIVER] = "r";
STATE_TAGS[STATE_HEAVY_WEIGHT] = "w";
STATE_TAGS[STATE_HOME] = "h";
STATE_TAGS[STATE_LAST_ACTIVITY] = "l";
STATE_TAGS[STATE_CACHED_ACTIVITY] = "a";
STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT] = "c";
STATE_TAGS[STATE_CACHED_EMPTY] = "e";
STATE_PROTO_ENUMS = new int[STATE_COUNT];
STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsEnums.PROCESS_STATE_PERSISTENT;
STATE_PROTO_ENUMS[STATE_TOP] = ProcessStatsEnums.PROCESS_STATE_TOP;
STATE_PROTO_ENUMS[STATE_IMPORTANT_FOREGROUND] =
ProcessStatsEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
STATE_PROTO_ENUMS[STATE_IMPORTANT_BACKGROUND] =
ProcessStatsEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
STATE_PROTO_ENUMS[STATE_BACKUP] = ProcessStatsEnums.PROCESS_STATE_BACKUP;
STATE_PROTO_ENUMS[STATE_SERVICE] = ProcessStatsEnums.PROCESS_STATE_SERVICE;
STATE_PROTO_ENUMS[STATE_SERVICE_RESTARTING] =
ProcessStatsEnums.PROCESS_STATE_SERVICE_RESTARTING;
STATE_PROTO_ENUMS[STATE_RECEIVER] = ProcessStatsEnums.PROCESS_STATE_RECEIVER;
STATE_PROTO_ENUMS[STATE_HEAVY_WEIGHT] = ProcessStatsEnums.PROCESS_STATE_HEAVY_WEIGHT;
STATE_PROTO_ENUMS[STATE_HOME] = ProcessStatsEnums.PROCESS_STATE_HOME;
STATE_PROTO_ENUMS[STATE_LAST_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_LAST_ACTIVITY;
STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY;
STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
}
public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
"off", "on"
};
public static final String[] ADJ_MEM_NAMES_CSV = new String[] {
"norm", "mod", "low", "crit"
};
// 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"
};
static final int[] ADJ_SCREEN_PROTO_ENUMS = new int[] {
ProcessStatsEnums.SCREEN_STATE_OFF,
ProcessStatsEnums.SCREEN_STATE_ON
};
static final String[] ADJ_MEM_TAGS = new String[] {
"n", "m", "l", "c"
};
static final int[] ADJ_MEM_PROTO_ENUMS = new int[] {
ProcessStatsEnums.MEMORY_STATE_NORMAL,
ProcessStatsEnums.MEMORY_STATE_MODERATE,
ProcessStatsEnums.MEMORY_STATE_LOW,
ProcessStatsEnums.MEMORY_STATE_CRITICAL
};
static final String CSV_SEP = "\t";
/**
* No instantiate
*/
private DumpUtils() {
}
public static void printScreenLabel(PrintWriter pw, int offset) {
switch (offset) {
case ADJ_NOTHING:
pw.print(" ");
break;
case ADJ_SCREEN_OFF:
pw.print("SOff/");
break;
case ADJ_SCREEN_ON:
pw.print(" SOn/");
break;
default:
pw.print("????/");
break;
}
}
public static void printScreenLabelCsv(PrintWriter pw, int offset) {
switch (offset) {
case ADJ_NOTHING:
break;
case ADJ_SCREEN_OFF:
pw.print(ADJ_SCREEN_NAMES_CSV[0]);
break;
case ADJ_SCREEN_ON:
pw.print(ADJ_SCREEN_NAMES_CSV[1]);
break;
default:
pw.print("???");
break;
}
}
public static void printMemLabel(PrintWriter pw, int offset, char sep) {
switch (offset) {
case ADJ_NOTHING:
pw.print(" ");
if (sep != 0) pw.print(' ');
break;
case ADJ_MEM_FACTOR_NORMAL:
pw.print("Norm");
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_MODERATE:
pw.print(" Mod");
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_LOW:
pw.print(" Low");
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_CRITICAL:
pw.print("Crit");
if (sep != 0) pw.print(sep);
break;
default:
pw.print("????");
if (sep != 0) pw.print(sep);
break;
}
}
public static void printMemLabelCsv(PrintWriter pw, int offset) {
if (offset >= ADJ_MEM_FACTOR_NORMAL) {
if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
pw.print(ADJ_MEM_NAMES_CSV[offset]);
} else {
pw.print("???");
}
}
}
public static void printPercent(PrintWriter pw, double fraction) {
fraction *= 100;
if (fraction < 1) {
pw.print(String.format("%.2f", fraction));
} else if (fraction < 10) {
pw.print(String.format("%.1f", fraction));
} else {
pw.print(String.format("%.0f", fraction));
}
pw.print("%");
}
public static void printProcStateTag(PrintWriter pw, int state) {
state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT);
state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT);
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_PROTO_ENUMS,
state, ADJ_SCREEN_MOD * STATE_COUNT);
state = printProto(proto, memId, ADJ_MEM_PROTO_ENUMS, state, STATE_COUNT);
printProto(proto, stateId, STATE_PROTO_ENUMS, 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);
}
public static void printProcStateAdjTagProto(ProtoOutputStream proto, long screenId, long memId,
int state) {
state = printProto(proto, screenId, ADJ_SCREEN_PROTO_ENUMS,
state, ADJ_SCREEN_MOD * STATE_COUNT);
printProto(proto, memId, ADJ_MEM_PROTO_ENUMS, state, STATE_COUNT);
}
public static void printProcStateDurationProto(ProtoOutputStream proto, long fieldId,
int procState, long duration) {
final long stateToken = proto.start(fieldId);
DumpUtils.printProto(proto, ProcessStatsStateProto.PROCESS_STATE,
DumpUtils.STATE_PROTO_ENUMS, procState, 1);
proto.write(ProcessStatsStateProto.DURATION_MS, duration);
proto.end(stateToken);
}
public static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
pw.print(',');
printProcStateTag(pw, state);
pw.print(':');
pw.print(value);
}
public static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
pw.print(',');
printAdjTag(pw, state);
pw.print(':');
pw.print(value);
}
public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
int curState, long curStartTime, long now) {
long totalTime = 0;
int printedScreen = -1;
for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
int printedMem = -1;
for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
int state = imem+iscreen;
long time = durations[state];
String running = "";
if (curState == state) {
time += now - curStartTime;
if (pw != null) {
running = " (running)";
}
}
if (time != 0) {
if (pw != null) {
pw.print(prefix);
printScreenLabel(pw, printedScreen != iscreen
? iscreen : STATE_NOTHING);
printedScreen = iscreen;
printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0);
printedMem = imem;
pw.print(": ");
TimeUtils.formatDuration(time, pw); pw.println(running);
}
totalTime += time;
}
}
}
if (totalTime != 0 && pw != null) {
pw.print(prefix);
pw.print(" TOTAL: ");
TimeUtils.formatDuration(totalTime, pw);
pw.println();
}
return totalTime;
}
public static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
int curState, long curStartTime, long now) {
for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
int state = imem+iscreen;
long time = durations[state];
if (curState == state) {
time += now - curStartTime;
}
if (time != 0) {
printAdjTagAndValue(pw, state, time);
}
}
}
}
private static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
int[] memStates, int[] procStates) {
final int NS = screenStates != null ? screenStates.length : 1;
final int NM = memStates != null ? memStates.length : 1;
final int NP = procStates != null ? procStates.length : 1;
for (int is=0; is<NS; is++) {
for (int im=0; im<NM; im++) {
for (int ip=0; ip<NP; ip++) {
pw.print(sep);
boolean printed = false;
if (screenStates != null && screenStates.length > 1) {
printScreenLabelCsv(pw, screenStates[is]);
printed = true;
}
if (memStates != null && memStates.length > 1) {
if (printed) {
pw.print("-");
}
printMemLabelCsv(pw, memStates[im]);
printed = true;
}
if (procStates != null && procStates.length > 1) {
if (printed) {
pw.print("-");
}
pw.print(STATE_NAMES_CSV[procStates[ip]]);
}
}
}
}
}
public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, String header,
ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
long now, long totalTime) {
for (int i=procs.size()-1; i>=0; i--) {
final ProcessState proc = procs.get(i);
proc.dumpSummary(pw, prefix, header, screenStates, memStates, procStates, now,
totalTime);
}
}
public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
boolean sepProcStates, int[] procStates, long now) {
pw.print("process");
pw.print(CSV_SEP);
pw.print("uid");
pw.print(CSV_SEP);
pw.print("vers");
dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
sepMemStates ? memStates : null,
sepProcStates ? procStates : null);
pw.println();
for (int i=procs.size()-1; i>=0; i--) {
ProcessState proc = procs.get(i);
pw.print(proc.getName());
pw.print(CSV_SEP);
UserHandle.formatUid(pw, proc.getUid());
pw.print(CSV_SEP);
pw.print(proc.getVersion());
proc.dumpCsv(pw, sepScreenStates, screenStates, sepMemStates,
memStates, sepProcStates, procStates, now);
pw.println();
}
}
public static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
int index = value/mod;
if (index >= 0 && index < array.length) {
pw.print(array[index]);
} else {
pw.print('?');
}
return value - index*mod;
}
public static int printProto(ProtoOutputStream proto, long fieldId,
int[] enums, int value, int mod) {
int index = value/mod;
if (index >= 0 && index < enums.length) {
proto.write(fieldId, enums[index]);
} // 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();
final int PKGLEN = pkgName.length();
if (ITEMLEN == PKGLEN) {
return "";
} else if (ITEMLEN >= PKGLEN) {
if (itemName.charAt(PKGLEN) == '.') {
return itemName.substring(PKGLEN);
}
}
}
return itemName;
}
}