1 /*
2  * Copyright (C) 2013 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 
17 package com.android.internal.app.procstats;
18 
19 import android.os.UserHandle;
20 import android.service.procstats.ProcessStatsEnums;
21 import android.service.procstats.ProcessStatsStateProto;
22 import android.util.TimeUtils;
23 import android.util.proto.ProtoOutputStream;
24 
25 import static com.android.internal.app.procstats.ProcessStats.*;
26 
27 import java.io.PrintWriter;
28 import java.util.ArrayList;
29 
30 /**
31  * Utilities for dumping.
32  */
33 public final class DumpUtils {
34     public static final String[] STATE_NAMES;
35     public static final String[] STATE_LABELS;
36     public static final String STATE_LABEL_TOTAL;
37     public static final String STATE_LABEL_CACHED;
38     public static final String[] STATE_NAMES_CSV;
39     static final String[] STATE_TAGS;
40     static final int[] STATE_PROTO_ENUMS;
41 
42     // Make the mapping easy to update.
43     static {
44         STATE_NAMES = new String[STATE_COUNT];
45         STATE_NAMES[STATE_PERSISTENT]               = "Persist";
46         STATE_NAMES[STATE_TOP]                      = "Top";
47         STATE_NAMES[STATE_IMPORTANT_FOREGROUND]     = "ImpFg";
48         STATE_NAMES[STATE_IMPORTANT_BACKGROUND]     = "ImpBg";
49         STATE_NAMES[STATE_BACKUP]                   = "Backup";
50         STATE_NAMES[STATE_SERVICE]                  = "Service";
51         STATE_NAMES[STATE_SERVICE_RESTARTING]       = "ServRst";
52         STATE_NAMES[STATE_RECEIVER]                 = "Receivr";
53         STATE_NAMES[STATE_HEAVY_WEIGHT]             = "HeavyWt";
54         STATE_NAMES[STATE_HOME]                     = "Home";
55         STATE_NAMES[STATE_LAST_ACTIVITY]            = "LastAct";
56         STATE_NAMES[STATE_CACHED_ACTIVITY]          = "CchAct";
57         STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT]   = "CchCAct";
58         STATE_NAMES[STATE_CACHED_EMPTY]             = "CchEmty";
59 
60         STATE_LABELS = new String[STATE_COUNT];
61         STATE_LABELS[STATE_PERSISTENT]              = "Persistent";
62         STATE_LABELS[STATE_TOP]                     = "       Top";
63         STATE_LABELS[STATE_IMPORTANT_FOREGROUND]    = "    Imp Fg";
64         STATE_LABELS[STATE_IMPORTANT_BACKGROUND]    = "    Imp Bg";
65         STATE_LABELS[STATE_BACKUP]                  = "    Backup";
66         STATE_LABELS[STATE_SERVICE]                 = "   Service";
67         STATE_LABELS[STATE_SERVICE_RESTARTING]      = "Service Rs";
68         STATE_LABELS[STATE_RECEIVER]                = "  Receiver";
69         STATE_LABELS[STATE_HEAVY_WEIGHT]            = " Heavy Wgt";
70         STATE_LABELS[STATE_HOME]                    = "    (Home)";
71         STATE_LABELS[STATE_LAST_ACTIVITY]           = "(Last Act)";
72         STATE_LABELS[STATE_CACHED_ACTIVITY]         = " (Cch Act)";
73         STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT]  = "(Cch CAct)";
74         STATE_LABELS[STATE_CACHED_EMPTY]            = "(Cch Emty)";
75         STATE_LABEL_CACHED                          = "  (Cached)";
76         STATE_LABEL_TOTAL                           = "     TOTAL";
77 
78         STATE_NAMES_CSV = new String[STATE_COUNT];
79         STATE_NAMES_CSV[STATE_PERSISTENT]               = "pers";
80         STATE_NAMES_CSV[STATE_TOP]                      = "top";
81         STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND]     = "impfg";
82         STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND]     = "impbg";
83         STATE_NAMES_CSV[STATE_BACKUP]                   = "backup";
84         STATE_NAMES_CSV[STATE_SERVICE]                  = "service";
85         STATE_NAMES_CSV[STATE_SERVICE_RESTARTING]       = "service-rs";
86         STATE_NAMES_CSV[STATE_RECEIVER]                 = "receiver";
87         STATE_NAMES_CSV[STATE_HEAVY_WEIGHT]             = "heavy";
88         STATE_NAMES_CSV[STATE_HOME]                     = "home";
89         STATE_NAMES_CSV[STATE_LAST_ACTIVITY]            = "lastact";
90         STATE_NAMES_CSV[STATE_CACHED_ACTIVITY]          = "cch-activity";
91         STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT]   = "cch-aclient";
92         STATE_NAMES_CSV[STATE_CACHED_EMPTY]             = "cch-empty";
93 
94         STATE_TAGS = new String[STATE_COUNT];
95         STATE_TAGS[STATE_PERSISTENT]                = "p";
96         STATE_TAGS[STATE_TOP]                       = "t";
97         STATE_TAGS[STATE_IMPORTANT_FOREGROUND]      = "f";
98         STATE_TAGS[STATE_IMPORTANT_BACKGROUND]      = "b";
99         STATE_TAGS[STATE_BACKUP]                    = "u";
100         STATE_TAGS[STATE_SERVICE]                   = "s";
101         STATE_TAGS[STATE_SERVICE_RESTARTING]        = "x";
102         STATE_TAGS[STATE_RECEIVER]                  = "r";
103         STATE_TAGS[STATE_HEAVY_WEIGHT]              = "w";
104         STATE_TAGS[STATE_HOME]                      = "h";
105         STATE_TAGS[STATE_LAST_ACTIVITY]             = "l";
106         STATE_TAGS[STATE_CACHED_ACTIVITY]           = "a";
107         STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT]    = "c";
108         STATE_TAGS[STATE_CACHED_EMPTY]              = "e";
109 
110         STATE_PROTO_ENUMS = new int[STATE_COUNT];
111         STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsEnums.PROCESS_STATE_PERSISTENT;
112         STATE_PROTO_ENUMS[STATE_TOP] = ProcessStatsEnums.PROCESS_STATE_TOP;
113         STATE_PROTO_ENUMS[STATE_IMPORTANT_FOREGROUND] =
114                 ProcessStatsEnums.PROCESS_STATE_IMPORTANT_FOREGROUND;
115         STATE_PROTO_ENUMS[STATE_IMPORTANT_BACKGROUND] =
116                 ProcessStatsEnums.PROCESS_STATE_IMPORTANT_BACKGROUND;
117         STATE_PROTO_ENUMS[STATE_BACKUP] = ProcessStatsEnums.PROCESS_STATE_BACKUP;
118         STATE_PROTO_ENUMS[STATE_SERVICE] = ProcessStatsEnums.PROCESS_STATE_SERVICE;
119         STATE_PROTO_ENUMS[STATE_SERVICE_RESTARTING] =
120                 ProcessStatsEnums.PROCESS_STATE_SERVICE_RESTARTING;
121         STATE_PROTO_ENUMS[STATE_RECEIVER] = ProcessStatsEnums.PROCESS_STATE_RECEIVER;
122         STATE_PROTO_ENUMS[STATE_HEAVY_WEIGHT] = ProcessStatsEnums.PROCESS_STATE_HEAVY_WEIGHT;
123         STATE_PROTO_ENUMS[STATE_HOME] = ProcessStatsEnums.PROCESS_STATE_HOME;
124         STATE_PROTO_ENUMS[STATE_LAST_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_LAST_ACTIVITY;
125         STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY] = ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY;
126         STATE_PROTO_ENUMS[STATE_CACHED_ACTIVITY_CLIENT] =
127                 ProcessStatsEnums.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
128         STATE_PROTO_ENUMS[STATE_CACHED_EMPTY] = ProcessStatsEnums.PROCESS_STATE_CACHED_EMPTY;
129     }
130 
131     public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
132             "off", "on"
133     };
134 
135     public static final String[] ADJ_MEM_NAMES_CSV = new String[] {
136             "norm", "mod",  "low", "crit"
137     };
138 
139     // State enum is defined in frameworks/base/core/proto/android/service/procstats.proto
140     // Update states must sync enum definition as well, the ordering must not be changed.
141     static final String[] ADJ_SCREEN_TAGS = new String[] {
142             "0", "1"
143     };
144 
145     static final int[] ADJ_SCREEN_PROTO_ENUMS = new int[] {
146             ProcessStatsEnums.SCREEN_STATE_OFF,
147             ProcessStatsEnums.SCREEN_STATE_ON
148     };
149 
150     static final String[] ADJ_MEM_TAGS = new String[] {
151             "n", "m",  "l", "c"
152     };
153 
154     static final int[] ADJ_MEM_PROTO_ENUMS = new int[] {
155             ProcessStatsEnums.MEMORY_STATE_NORMAL,
156             ProcessStatsEnums.MEMORY_STATE_MODERATE,
157             ProcessStatsEnums.MEMORY_STATE_LOW,
158             ProcessStatsEnums.MEMORY_STATE_CRITICAL
159     };
160 
161     static final String CSV_SEP = "\t";
162 
163     /**
164      * No instantiate
165      */
DumpUtils()166     private DumpUtils() {
167     }
168 
printScreenLabel(PrintWriter pw, int offset)169     public static void printScreenLabel(PrintWriter pw, int offset) {
170         switch (offset) {
171             case ADJ_NOTHING:
172                 pw.print("     ");
173                 break;
174             case ADJ_SCREEN_OFF:
175                 pw.print("SOff/");
176                 break;
177             case ADJ_SCREEN_ON:
178                 pw.print(" SOn/");
179                 break;
180             default:
181                 pw.print("????/");
182                 break;
183         }
184     }
185 
printScreenLabelCsv(PrintWriter pw, int offset)186     public static void printScreenLabelCsv(PrintWriter pw, int offset) {
187         switch (offset) {
188             case ADJ_NOTHING:
189                 break;
190             case ADJ_SCREEN_OFF:
191                 pw.print(ADJ_SCREEN_NAMES_CSV[0]);
192                 break;
193             case ADJ_SCREEN_ON:
194                 pw.print(ADJ_SCREEN_NAMES_CSV[1]);
195                 break;
196             default:
197                 pw.print("???");
198                 break;
199         }
200     }
201 
printMemLabel(PrintWriter pw, int offset, char sep)202     public static void printMemLabel(PrintWriter pw, int offset, char sep) {
203         switch (offset) {
204             case ADJ_NOTHING:
205                 pw.print("    ");
206                 if (sep != 0) pw.print(' ');
207                 break;
208             case ADJ_MEM_FACTOR_NORMAL:
209                 pw.print("Norm");
210                 if (sep != 0) pw.print(sep);
211                 break;
212             case ADJ_MEM_FACTOR_MODERATE:
213                 pw.print(" Mod");
214                 if (sep != 0) pw.print(sep);
215                 break;
216             case ADJ_MEM_FACTOR_LOW:
217                 pw.print(" Low");
218                 if (sep != 0) pw.print(sep);
219                 break;
220             case ADJ_MEM_FACTOR_CRITICAL:
221                 pw.print("Crit");
222                 if (sep != 0) pw.print(sep);
223                 break;
224             default:
225                 pw.print("????");
226                 if (sep != 0) pw.print(sep);
227                 break;
228         }
229     }
230 
printMemLabelCsv(PrintWriter pw, int offset)231     public static void printMemLabelCsv(PrintWriter pw, int offset) {
232         if (offset >= ADJ_MEM_FACTOR_NORMAL) {
233             if (offset <= ADJ_MEM_FACTOR_CRITICAL) {
234                 pw.print(ADJ_MEM_NAMES_CSV[offset]);
235             } else {
236                 pw.print("???");
237             }
238         }
239     }
240 
printPercent(PrintWriter pw, double fraction)241     public static void printPercent(PrintWriter pw, double fraction) {
242         fraction *= 100;
243         if (fraction < 1) {
244             pw.print(String.format("%.2f", fraction));
245         } else if (fraction < 10) {
246             pw.print(String.format("%.1f", fraction));
247         } else {
248             pw.print(String.format("%.0f", fraction));
249         }
250         pw.print("%");
251     }
252 
printProcStateTag(PrintWriter pw, int state)253     public static void printProcStateTag(PrintWriter pw, int state) {
254         state = printArrayEntry(pw, ADJ_SCREEN_TAGS,  state, ADJ_SCREEN_MOD*STATE_COUNT);
255         state = printArrayEntry(pw, ADJ_MEM_TAGS,  state, STATE_COUNT);
256         printArrayEntry(pw, STATE_TAGS,  state, 1);
257     }
258 
printProcStateTagProto(ProtoOutputStream proto, long screenId, long memId, long stateId, int state)259     public static void printProcStateTagProto(ProtoOutputStream proto, long screenId, long memId,
260             long stateId, int state) {
261         state = printProto(proto, screenId, ADJ_SCREEN_PROTO_ENUMS,
262                 state, ADJ_SCREEN_MOD * STATE_COUNT);
263         state = printProto(proto, memId, ADJ_MEM_PROTO_ENUMS, state, STATE_COUNT);
264         printProto(proto, stateId, STATE_PROTO_ENUMS, state, 1);
265     }
266 
printAdjTag(PrintWriter pw, int state)267     public static void printAdjTag(PrintWriter pw, int state) {
268         state = printArrayEntry(pw, ADJ_SCREEN_TAGS,  state, ADJ_SCREEN_MOD);
269         printArrayEntry(pw, ADJ_MEM_TAGS, state, 1);
270     }
271 
printProcStateAdjTagProto(ProtoOutputStream proto, long screenId, long memId, int state)272     public static void printProcStateAdjTagProto(ProtoOutputStream proto, long screenId, long memId,
273             int state) {
274         state = printProto(proto, screenId, ADJ_SCREEN_PROTO_ENUMS,
275                 state, ADJ_SCREEN_MOD * STATE_COUNT);
276         printProto(proto, memId, ADJ_MEM_PROTO_ENUMS, state, STATE_COUNT);
277     }
278 
printProcStateDurationProto(ProtoOutputStream proto, long fieldId, int procState, long duration)279     public static void printProcStateDurationProto(ProtoOutputStream proto, long fieldId,
280             int procState, long duration) {
281         final long stateToken = proto.start(fieldId);
282         DumpUtils.printProto(proto, ProcessStatsStateProto.PROCESS_STATE,
283                 DumpUtils.STATE_PROTO_ENUMS, procState, 1);
284         proto.write(ProcessStatsStateProto.DURATION_MS, duration);
285         proto.end(stateToken);
286     }
287 
printProcStateTagAndValue(PrintWriter pw, int state, long value)288     public static void printProcStateTagAndValue(PrintWriter pw, int state, long value) {
289         pw.print(',');
290         printProcStateTag(pw, state);
291         pw.print(':');
292         pw.print(value);
293     }
294 
printAdjTagAndValue(PrintWriter pw, int state, long value)295     public static void printAdjTagAndValue(PrintWriter pw, int state, long value) {
296         pw.print(',');
297         printAdjTag(pw, state);
298         pw.print(':');
299         pw.print(value);
300     }
301 
dumpSingleTime(PrintWriter pw, String prefix, long[] durations, int curState, long curStartTime, long now)302     public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations,
303             int curState, long curStartTime, long now) {
304         long totalTime = 0;
305         int printedScreen = -1;
306         for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
307             int printedMem = -1;
308             for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
309                 int state = imem+iscreen;
310                 long time = durations[state];
311                 String running = "";
312                 if (curState == state) {
313                     time += now - curStartTime;
314                     if (pw != null) {
315                         running = " (running)";
316                     }
317                 }
318                 if (time != 0) {
319                     if (pw != null) {
320                         pw.print(prefix);
321                         printScreenLabel(pw, printedScreen != iscreen
322                                 ? iscreen : STATE_NOTHING);
323                         printedScreen = iscreen;
324                         printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0);
325                         printedMem = imem;
326                         pw.print(": ");
327                         TimeUtils.formatDuration(time, pw); pw.println(running);
328                     }
329                     totalTime += time;
330                 }
331             }
332         }
333         if (totalTime != 0 && pw != null) {
334             pw.print(prefix);
335             pw.print("    TOTAL: ");
336             TimeUtils.formatDuration(totalTime, pw);
337             pw.println();
338         }
339         return totalTime;
340     }
341 
dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations, int curState, long curStartTime, long now)342     public static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations,
343             int curState, long curStartTime, long now) {
344         for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) {
345             for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) {
346                 int state = imem+iscreen;
347                 long time = durations[state];
348                 if (curState == state) {
349                     time += now - curStartTime;
350                 }
351                 if (time != 0) {
352                     printAdjTagAndValue(pw, state, time);
353                 }
354             }
355         }
356     }
357 
dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates, int[] memStates, int[] procStates)358     private static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
359             int[] memStates, int[] procStates) {
360         final int NS = screenStates != null ? screenStates.length : 1;
361         final int NM = memStates != null ? memStates.length : 1;
362         final int NP = procStates != null ? procStates.length : 1;
363         for (int is=0; is<NS; is++) {
364             for (int im=0; im<NM; im++) {
365                 for (int ip=0; ip<NP; ip++) {
366                     pw.print(sep);
367                     boolean printed = false;
368                     if (screenStates != null && screenStates.length > 1) {
369                         printScreenLabelCsv(pw, screenStates[is]);
370                         printed = true;
371                     }
372                     if (memStates != null && memStates.length > 1) {
373                         if (printed) {
374                             pw.print("-");
375                         }
376                         printMemLabelCsv(pw, memStates[im]);
377                         printed = true;
378                     }
379                     if (procStates != null && procStates.length > 1) {
380                         if (printed) {
381                             pw.print("-");
382                         }
383                         pw.print(STATE_NAMES_CSV[procStates[ip]]);
384                     }
385                 }
386             }
387         }
388     }
389 
dumpProcessSummaryLocked(PrintWriter pw, String prefix, String header, ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates, long now, long totalTime)390     public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, String header,
391             ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates,
392             long now, long totalTime) {
393         for (int i=procs.size()-1; i>=0; i--) {
394             final ProcessState proc = procs.get(i);
395             proc.dumpSummary(pw, prefix, header, screenStates, memStates, procStates, now,
396                     totalTime);
397         }
398     }
399 
dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now)400     public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs,
401             boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates,
402             boolean sepProcStates, int[] procStates, long now) {
403         pw.print("process");
404         pw.print(CSV_SEP);
405         pw.print("uid");
406         pw.print(CSV_SEP);
407         pw.print("vers");
408         dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null,
409                 sepMemStates ? memStates : null,
410                 sepProcStates ? procStates : null);
411         pw.println();
412         for (int i=procs.size()-1; i>=0; i--) {
413             ProcessState proc = procs.get(i);
414             pw.print(proc.getName());
415             pw.print(CSV_SEP);
416             UserHandle.formatUid(pw, proc.getUid());
417             pw.print(CSV_SEP);
418             pw.print(proc.getVersion());
419             proc.dumpCsv(pw, sepScreenStates, screenStates, sepMemStates,
420                     memStates, sepProcStates, procStates, now);
421             pw.println();
422         }
423     }
424 
printArrayEntry(PrintWriter pw, String[] array, int value, int mod)425     public static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) {
426         int index = value/mod;
427         if (index >= 0 && index < array.length) {
428             pw.print(array[index]);
429         } else {
430             pw.print('?');
431         }
432         return value - index*mod;
433     }
434 
printProto(ProtoOutputStream proto, long fieldId, int[] enums, int value, int mod)435     public static int printProto(ProtoOutputStream proto, long fieldId,
436             int[] enums, int value, int mod) {
437         int index = value/mod;
438         if (index >= 0 && index < enums.length) {
439             proto.write(fieldId, enums[index]);
440         } // else enum default is always zero in proto3
441         return value - index*mod;
442     }
443 
collapseString(String pkgName, String itemName)444     public static String collapseString(String pkgName, String itemName) {
445         if (itemName.startsWith(pkgName)) {
446             final int ITEMLEN = itemName.length();
447             final int PKGLEN = pkgName.length();
448             if (ITEMLEN == PKGLEN) {
449                 return "";
450             } else if (ITEMLEN >= PKGLEN) {
451                 if (itemName.charAt(PKGLEN) == '.') {
452                     return itemName.substring(PKGLEN);
453                 }
454             }
455         }
456         return itemName;
457     }
458 }
459