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 
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import android.os.SystemClock;
23 import android.os.SystemProperties;
24 import android.os.UserHandle;
25 import android.text.format.DateFormat;
26 import android.util.ArrayMap;
27 import android.util.ArraySet;
28 import android.util.DebugUtils;
29 import android.util.Log;
30 import android.util.Slog;
31 import android.util.SparseArray;
32 import android.util.TimeUtils;
33 
34 import com.android.internal.app.procstats.ProcessStats;
35 import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
36 
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.PrintWriter;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collections;
43 import java.util.Comparator;
44 import java.util.Objects;
45 
46 public final class ServiceState {
47     private static final String TAG = "ProcessStats";
48     private static final boolean DEBUG = false;
49 
50     public static final int SERVICE_RUN = 0;
51     public static final int SERVICE_STARTED = 1;
52     public static final int SERVICE_BOUND = 2;
53     public static final int SERVICE_EXEC = 3;
54     public static final int SERVICE_COUNT = 4;
55 
56     private final String mPackage;
57     private final String mProcessName;
58     private final String mName;
59     private final DurationsTable mDurations;
60 
61     private ProcessState mProc;
62     private Object mOwner;
63 
64     private int mRunCount;
65     private int mRunState = STATE_NOTHING;
66     private long mRunStartTime;
67 
68     private boolean mStarted;
69     private boolean mRestarting;
70     private int mStartedCount;
71     private int mStartedState = STATE_NOTHING;
72     private long mStartedStartTime;
73 
74     private int mBoundCount;
75     private int mBoundState = STATE_NOTHING;
76     private long mBoundStartTime;
77 
78     private int mExecCount;
79     private int mExecState = STATE_NOTHING;
80     private long mExecStartTime;
81 
ServiceState(ProcessStats processStats, String pkg, String name, String processName, ProcessState proc)82     public ServiceState(ProcessStats processStats, String pkg, String name,
83             String processName, ProcessState proc) {
84         mPackage = pkg;
85         mName = name;
86         mProcessName = processName;
87         mProc = proc;
88         mDurations = new DurationsTable(processStats.mTableData);
89     }
90 
getPackage()91     public String getPackage() {
92         return mPackage;
93     }
94 
getProcessName()95     public String getProcessName() {
96         return mProcessName;
97     }
98 
getName()99     public String getName() {
100         return mName;
101     }
102 
getProcess()103     public ProcessState getProcess() {
104         return mProc;
105     }
106 
setProcess(ProcessState proc)107     public void setProcess(ProcessState proc) {
108         mProc = proc;
109     }
110 
setMemFactor(int memFactor, long now)111     public void setMemFactor(int memFactor, long now) {
112         if (isRestarting()) {
113             setRestarting(true, memFactor, now);
114         } else if (isInUse()) {
115             if (mStartedState != ProcessStats.STATE_NOTHING) {
116                 setStarted(true, memFactor, now);
117             }
118             if (mBoundState != ProcessStats.STATE_NOTHING) {
119                 setBound(true, memFactor, now);
120             }
121             if (mExecState != ProcessStats.STATE_NOTHING) {
122                 setExecuting(true, memFactor, now);
123             }
124         }
125     }
126 
applyNewOwner(Object newOwner)127     public void applyNewOwner(Object newOwner) {
128         if (mOwner != newOwner) {
129             if (mOwner == null) {
130                 mOwner = newOwner;
131                 mProc.incActiveServices(mName);
132             } else {
133                 // There was already an old owner, reset this object for its
134                 // new owner.
135                 mOwner = newOwner;
136                 if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
137                     long now = SystemClock.uptimeMillis();
138                     if (mStarted) {
139                         if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
140                                 + " from " + mOwner + " while started: pkg="
141                                 + mPackage + " service=" + mName + " proc=" + mProc);
142                         setStarted(false, 0, now);
143                     }
144                     if (mBoundState != STATE_NOTHING) {
145                         if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
146                                 + " from " + mOwner + " while bound: pkg="
147                                 + mPackage + " service=" + mName + " proc=" + mProc);
148                         setBound(false, 0, now);
149                     }
150                     if (mExecState != STATE_NOTHING) {
151                         if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner
152                                 + " from " + mOwner + " while executing: pkg="
153                                 + mPackage + " service=" + mName + " proc=" + mProc);
154                         setExecuting(false, 0, now);
155                     }
156                 }
157             }
158         }
159     }
160 
clearCurrentOwner(Object owner, boolean silently)161     public void clearCurrentOwner(Object owner, boolean silently) {
162         if (mOwner == owner) {
163             mProc.decActiveServices(mName);
164             if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) {
165                 long now = SystemClock.uptimeMillis();
166                 if (mStarted) {
167                     if (!silently) {
168                         Slog.wtfStack(TAG, "Service owner " + owner
169                                 + " cleared while started: pkg=" + mPackage + " service="
170                                 + mName + " proc=" + mProc);
171                     }
172                     setStarted(false, 0, now);
173                 }
174                 if (mBoundState != STATE_NOTHING) {
175                     if (!silently) {
176                         Slog.wtfStack(TAG, "Service owner " + owner
177                                 + " cleared while bound: pkg=" + mPackage + " service="
178                                 + mName + " proc=" + mProc);
179                     }
180                     setBound(false, 0, now);
181                 }
182                 if (mExecState != STATE_NOTHING) {
183                     if (!silently) {
184                         Slog.wtfStack(TAG, "Service owner " + owner
185                                 + " cleared while exec: pkg=" + mPackage + " service="
186                                 + mName + " proc=" + mProc);
187                     }
188                     setExecuting(false, 0, now);
189                 }
190             }
191             mOwner = null;
192         }
193     }
194 
isInUse()195     public boolean isInUse() {
196         return mOwner != null || mRestarting;
197     }
198 
isRestarting()199     public boolean isRestarting() {
200         return mRestarting;
201     }
202 
add(ServiceState other)203     public void add(ServiceState other) {
204         mDurations.addDurations(other.mDurations);
205         mRunCount += other.mRunCount;
206         mStartedCount += other.mStartedCount;
207         mBoundCount += other.mBoundCount;
208         mExecCount += other.mExecCount;
209     }
210 
resetSafely(long now)211     public void resetSafely(long now) {
212         mDurations.resetTable();
213         mRunCount = mRunState != STATE_NOTHING ? 1 : 0;
214         mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0;
215         mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0;
216         mExecCount = mExecState != STATE_NOTHING ? 1 : 0;
217         mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now;
218     }
219 
writeToParcel(Parcel out, long now)220     public void writeToParcel(Parcel out, long now) {
221         mDurations.writeToParcel(out);
222         out.writeInt(mRunCount);
223         out.writeInt(mStartedCount);
224         out.writeInt(mBoundCount);
225         out.writeInt(mExecCount);
226     }
227 
readFromParcel(Parcel in)228     public boolean readFromParcel(Parcel in) {
229         if (!mDurations.readFromParcel(in)) {
230             return false;
231         }
232         mRunCount = in.readInt();
233         mStartedCount = in.readInt();
234         mBoundCount = in.readInt();
235         mExecCount = in.readInt();
236         return true;
237     }
238 
commitStateTime(long now)239     public void commitStateTime(long now) {
240         if (mRunState != STATE_NOTHING) {
241             mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
242                     now - mRunStartTime);
243             mRunStartTime = now;
244         }
245         if (mStartedState != STATE_NOTHING) {
246             mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
247                     now - mStartedStartTime);
248             mStartedStartTime = now;
249         }
250         if (mBoundState != STATE_NOTHING) {
251             mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
252                     now - mBoundStartTime);
253             mBoundStartTime = now;
254         }
255         if (mExecState != STATE_NOTHING) {
256             mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT),
257                     now - mExecStartTime);
258             mExecStartTime = now;
259         }
260     }
261 
updateRunning(int memFactor, long now)262     private void updateRunning(int memFactor, long now) {
263         final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING
264                 || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING;
265         if (mRunState != state) {
266             if (mRunState != STATE_NOTHING) {
267                 mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT),
268                         now - mRunStartTime);
269             } else if (state != STATE_NOTHING) {
270                 mRunCount++;
271             }
272             mRunState = state;
273             mRunStartTime = now;
274         }
275     }
276 
setStarted(boolean started, int memFactor, long now)277     public void setStarted(boolean started, int memFactor, long now) {
278         if (mOwner == null) {
279             Slog.wtf(TAG, "Starting service " + this + " without owner");
280         }
281         mStarted = started;
282         updateStartedState(memFactor, now);
283     }
284 
setRestarting(boolean restarting, int memFactor, long now)285     public void setRestarting(boolean restarting, int memFactor, long now) {
286         mRestarting = restarting;
287         updateStartedState(memFactor, now);
288     }
289 
updateStartedState(int memFactor, long now)290     public void updateStartedState(int memFactor, long now) {
291         final boolean wasStarted = mStartedState != STATE_NOTHING;
292         final boolean started = mStarted || mRestarting;
293         final int state = started ? memFactor : STATE_NOTHING;
294         if (mStartedState != state) {
295             if (mStartedState != STATE_NOTHING) {
296                 mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT),
297                         now - mStartedStartTime);
298             } else if (started) {
299                 mStartedCount++;
300             }
301             mStartedState = state;
302             mStartedStartTime = now;
303             mProc = mProc.pullFixedProc(mPackage);
304             if (wasStarted != started) {
305                 if (started) {
306                     mProc.incStartedServices(memFactor, now, mName);
307                 } else {
308                     mProc.decStartedServices(memFactor, now, mName);
309                 }
310             }
311             updateRunning(memFactor, now);
312         }
313     }
314 
setBound(boolean bound, int memFactor, long now)315     public void setBound(boolean bound, int memFactor, long now) {
316         if (mOwner == null) {
317             Slog.wtf(TAG, "Binding service " + this + " without owner");
318         }
319         final int state = bound ? memFactor : STATE_NOTHING;
320         if (mBoundState != state) {
321             if (mBoundState != STATE_NOTHING) {
322                 mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT),
323                         now - mBoundStartTime);
324             } else if (bound) {
325                 mBoundCount++;
326             }
327             mBoundState = state;
328             mBoundStartTime = now;
329             updateRunning(memFactor, now);
330         }
331     }
332 
setExecuting(boolean executing, int memFactor, long now)333     public void setExecuting(boolean executing, int memFactor, long now) {
334         if (mOwner == null) {
335             Slog.wtf(TAG, "Executing service " + this + " without owner");
336         }
337         final int state = executing ? memFactor : STATE_NOTHING;
338         if (mExecState != state) {
339             if (mExecState != STATE_NOTHING) {
340                 mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT),
341                         now - mExecStartTime);
342             } else if (executing) {
343                 mExecCount++;
344             }
345             mExecState = state;
346             mExecStartTime = now;
347             updateRunning(memFactor, now);
348         }
349     }
350 
getDuration(int opType, int curState, long startTime, int memFactor, long now)351     public long getDuration(int opType, int curState, long startTime, int memFactor,
352             long now) {
353         int state = opType + (memFactor*SERVICE_COUNT);
354         long time = mDurations.getValueForId((byte)state);
355         if (curState == memFactor) {
356             time += now - startTime;
357         }
358         return time;
359     }
360 
dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix, long now, long totalTime, boolean dumpSummary, boolean dumpAll)361     public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
362             long now, long totalTime, boolean dumpSummary, boolean dumpAll) {
363         dumpStats(pw, prefix, prefixInner, headerPrefix, "Running",
364                 mRunCount, ServiceState.SERVICE_RUN, mRunState,
365                 mRunStartTime, now, totalTime, !dumpSummary || dumpAll);
366         dumpStats(pw, prefix, prefixInner, headerPrefix, "Started",
367                 mStartedCount, ServiceState.SERVICE_STARTED, mStartedState,
368                 mStartedStartTime, now, totalTime, !dumpSummary || dumpAll);
369         dumpStats(pw, prefix, prefixInner, headerPrefix, "Bound",
370                 mBoundCount, ServiceState.SERVICE_BOUND, mBoundState,
371                 mBoundStartTime, now, totalTime, !dumpSummary || dumpAll);
372         dumpStats(pw, prefix, prefixInner, headerPrefix, "Executing",
373                 mExecCount, ServiceState.SERVICE_EXEC, mExecState,
374                 mExecStartTime, now, totalTime, !dumpSummary || dumpAll);
375         if (dumpAll) {
376             if (mOwner != null) {
377                 pw.print("        mOwner="); pw.println(mOwner);
378             }
379             if (mStarted || mRestarting) {
380                 pw.print("        mStarted="); pw.print(mStarted);
381                 pw.print(" mRestarting="); pw.println(mRestarting);
382             }
383         }
384     }
385 
dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix, String header, int count, int serviceType, int state, long startTime, long now, long totalTime, boolean dumpAll)386     private void dumpStats(PrintWriter pw, String prefix, String prefixInner,
387             String headerPrefix, String header,
388             int count, int serviceType, int state, long startTime, long now, long totalTime,
389             boolean dumpAll) {
390         if (count != 0) {
391             if (dumpAll) {
392                 pw.print(prefix); pw.print(header);
393                 pw.print(" op count "); pw.print(count); pw.println(":");
394                 dumpTime(pw, prefixInner, serviceType, state, startTime, now);
395             } else {
396                 long myTime = dumpTime(null, null, serviceType, state, startTime, now);
397                 pw.print(prefix); pw.print(headerPrefix); pw.print(header);
398                 pw.print(" count "); pw.print(count);
399                 pw.print(" / time ");
400                 DumpUtils.printPercent(pw, (double)myTime/(double)totalTime);
401                 pw.println();
402             }
403         }
404     }
405 
dumpTime(PrintWriter pw, String prefix, int serviceType, int curState, long curStartTime, long now)406     public long dumpTime(PrintWriter pw, String prefix,
407             int serviceType, int curState, long curStartTime, long now) {
408         long totalTime = 0;
409         int printedScreen = -1;
410         for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) {
411             int printedMem = -1;
412             for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) {
413                 int state = imem+iscreen;
414                 long time = getDuration(serviceType, curState, curStartTime, state, now);
415                 String running = "";
416                 if (curState == state && pw != null) {
417                     running = " (running)";
418                 }
419                 if (time != 0) {
420                     if (pw != null) {
421                         pw.print(prefix);
422                         DumpUtils.printScreenLabel(pw, printedScreen != iscreen
423                                 ? iscreen : STATE_NOTHING);
424                         printedScreen = iscreen;
425                         DumpUtils.printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING,
426                                 (char)0);
427                         printedMem = imem;
428                         pw.print(": ");
429                         TimeUtils.formatDuration(time, pw); pw.println(running);
430                     }
431                     totalTime += time;
432                 }
433             }
434         }
435         if (totalTime != 0 && pw != null) {
436             pw.print(prefix);
437             pw.print("    TOTAL: ");
438             TimeUtils.formatDuration(totalTime, pw);
439             pw.println();
440         }
441         return totalTime;
442     }
443 
dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, int vers, String serviceName, long now)444     public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, int vers,
445             String serviceName, long now) {
446         dumpTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName,
447                 ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now);
448         dumpTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName,
449                 ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now);
450         dumpTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName,
451                 ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now);
452         dumpTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName,
453                 ServiceState.SERVICE_EXEC, mExecCount, mExecState, mExecStartTime, now);
454     }
455 
dumpTimeCheckin(PrintWriter pw, String label, String packageName, int uid, int vers, String serviceName, int serviceType, int opCount, int curState, long curStartTime, long now)456     private void dumpTimeCheckin(PrintWriter pw, String label, String packageName,
457             int uid, int vers, String serviceName, int serviceType, int opCount,
458             int curState, long curStartTime, long now) {
459         if (opCount <= 0) {
460             return;
461         }
462         pw.print(label);
463         pw.print(",");
464         pw.print(packageName);
465         pw.print(",");
466         pw.print(uid);
467         pw.print(",");
468         pw.print(vers);
469         pw.print(",");
470         pw.print(serviceName);
471         pw.print(",");
472         pw.print(opCount);
473         boolean didCurState = false;
474         final int N = mDurations.getKeyCount();
475         for (int i=0; i<N; i++) {
476             final int key = mDurations.getKeyAt(i);
477             long time = mDurations.getValue(key);
478             int type = SparseMappingTable.getIdFromKey(key);
479             int memFactor = type / ServiceState.SERVICE_COUNT;
480             type %= ServiceState.SERVICE_COUNT;
481             if (type != serviceType) {
482                 continue;
483             }
484             if (curState == memFactor) {
485                 didCurState = true;
486                 time += now - curStartTime;
487             }
488             DumpUtils.printAdjTagAndValue(pw, memFactor, time);
489         }
490         if (!didCurState && curState != STATE_NOTHING) {
491             DumpUtils.printAdjTagAndValue(pw, curState, now - curStartTime);
492         }
493         pw.println();
494     }
495 
496 
toString()497     public String toString() {
498         return "ServiceState{" + Integer.toHexString(System.identityHashCode(this))
499                 + " " + mName + " pkg=" + mPackage + " proc="
500                 + Integer.toHexString(System.identityHashCode(this)) + "}";
501     }
502 }
503