• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.Parcel;
22 import android.os.Parcelable;
23 import android.os.SystemClock;
24 import android.os.UserHandle;
25 import android.service.procstats.PackageAssociationProcessStatsProto;
26 import android.service.procstats.PackageAssociationSourceProcessStatsProto;
27 import android.util.ArrayMap;
28 import android.util.Pair;
29 import android.util.Slog;
30 import android.util.TimeUtils;
31 import android.util.proto.ProtoOutputStream;
32 
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.Comparator;
37 import java.util.Objects;
38 
39 public final class AssociationState {
40     private static final String TAG = "ProcessStats";
41     private static final boolean DEBUG = false;
42 
43     private static final boolean VALIDATE_TIMES = false;
44 
45     private final ProcessStats mProcessStats;
46     private final ProcessStats.PackageState mPackageState;
47     private final String mProcessName;
48     private final String mName;
49 
50     private int mTotalNesting;
51     private long mTotalStartUptime;
52     private int mTotalCount;
53     private long mTotalDuration;
54     private int mTotalActiveNesting;
55     private long mTotalActiveStartUptime;
56     private int mTotalActiveCount;
57     private long mTotalActiveDuration;
58 
59     /**
60      * The state of the source process of an association.
61      */
62     public static final class SourceState implements Parcelable {
63         private @NonNull final ProcessStats mProcessStats;
64         private @Nullable final AssociationState mAssociationState;
65         private @Nullable final ProcessState mTargetProcess;
66         private @Nullable SourceState mCommonSourceState;
67         final SourceKey mKey;
68         int mProcStateSeq = -1;
69         int mProcState = ProcessStats.STATE_NOTHING;
70         boolean mInTrackingList;
71         int mNesting;
72         int mCount;
73         long mStartUptime;
74         long mDuration;
75         long mTrackingUptime;
76         int mActiveNesting;
77         int mActiveCount;
78         int mActiveProcState = ProcessStats.STATE_NOTHING;
79         long mActiveStartUptime;
80         long mActiveDuration;
81         DurationsTable mActiveDurations;
82 
SourceState(@onNull ProcessStats processStats, @Nullable AssociationState associationState, @NonNull ProcessState targetProcess, SourceKey key)83         SourceState(@NonNull ProcessStats processStats, @Nullable AssociationState associationState,
84                 @NonNull ProcessState targetProcess, SourceKey key) {
85             mProcessStats = processStats;
86             mAssociationState = associationState;
87             mTargetProcess = targetProcess;
88             mKey = key;
89         }
90 
91         @Nullable
getAssociationState()92         public AssociationState getAssociationState() {
93             return mAssociationState;
94         }
95 
getProcessName()96         public String getProcessName() {
97             return mKey.mProcess;
98         }
99 
getUid()100         public int getUid() {
101             return mKey.mUid;
102         }
103 
104         @Nullable
getCommonSourceState(boolean createIfNeeded)105         private SourceState getCommonSourceState(boolean createIfNeeded) {
106             if (mCommonSourceState == null && createIfNeeded) {
107                 mCommonSourceState = mTargetProcess.getOrCreateSourceState(mKey);
108             }
109             return mCommonSourceState;
110         }
111 
trackProcState(int procState, int seq, long now)112         public void trackProcState(int procState, int seq, long now) {
113             final int processState = procState;
114             procState = ProcessState.PROCESS_STATE_TO_STATE[procState];
115             if (seq != mProcStateSeq) {
116                 mProcStateSeq = seq;
117                 mProcState = procState;
118             } else if (procState < mProcState) {
119                 mProcState = procState;
120             }
121             if (procState < ProcessStats.STATE_HOME) {
122                 // If the proc state has become better than cached, then we want to
123                 // start tracking it to count when it is actually active.  If it drops
124                 // down to cached, we will clean it up when we later evaluate all currently
125                 // tracked associations in ProcessStats.updateTrackingAssociationsLocked().
126                 if (!mInTrackingList) {
127                     mInTrackingList = true;
128                     mTrackingUptime = now;
129                     if (mAssociationState != null) {
130                         mProcessStats.mTrackingAssociations.add(this);
131                     }
132                 }
133             }
134             if (mAssociationState != null) {
135                 final SourceState commonSource = getCommonSourceState(true);
136                 if (commonSource != null) {
137                     commonSource.trackProcState(processState, seq, now);
138                 }
139             }
140         }
141 
start()142         long start() {
143             final long now = start(-1);
144             if (mAssociationState != null) {
145                 final SourceState commonSource = getCommonSourceState(true);
146                 if (commonSource != null) {
147                     commonSource.start(now);
148                 }
149             }
150             return now;
151         }
152 
start(long now)153         long start(long now) {
154             mNesting++;
155             if (mNesting == 1) {
156                 if (now < 0) {
157                     now = SystemClock.uptimeMillis();
158                 }
159                 mCount++;
160                 mStartUptime = now;
161             }
162             return now;
163         }
164 
stop()165         public void stop() {
166             final long now = stop(-1);
167             if (mAssociationState != null) {
168                 final SourceState commonSource = getCommonSourceState(false);
169                 if (commonSource != null) {
170                     commonSource.stop(now);
171                 }
172             }
173         }
174 
stop(long now)175         long stop(long now) {
176             mNesting--;
177             if (mNesting == 0) {
178                 if (now < 0) {
179                     now = SystemClock.uptimeMillis();
180                 }
181                 mDuration += now - mStartUptime;
182                 stopTracking(now);
183             }
184             return now;
185         }
186 
startActive(long now)187         void startActive(long now) {
188             boolean startActive = false;
189             if (mInTrackingList) {
190                 if (mActiveStartUptime == 0) {
191                     mActiveStartUptime = now;
192                     mActiveNesting++;
193                     mActiveCount++;
194                     startActive = true;
195                     if (mAssociationState != null) {
196                         mAssociationState.mTotalActiveNesting++;
197                         if (mAssociationState.mTotalActiveNesting == 1) {
198                             mAssociationState.mTotalActiveCount++;
199                             mAssociationState.mTotalActiveStartUptime = now;
200                         }
201                     }
202                 } else if (mAssociationState == null) {
203                     mActiveNesting++;
204                 }
205                 if (mActiveProcState != mProcState) {
206                     if (mActiveProcState != ProcessStats.STATE_NOTHING) {
207                         // Currently active proc state changed, need to store the duration
208                         // so far and switch tracking to the new proc state.
209                         final long addedDuration = mActiveDuration + now - mActiveStartUptime;
210                         mActiveStartUptime = now;
211                         if (mAssociationState != null) {
212                             startActive = true;
213                         }
214                         if (addedDuration != 0) {
215                             if (mActiveDurations == null) {
216                                 makeDurations();
217                             }
218                             mActiveDurations.addDuration(mActiveProcState, addedDuration);
219                             mActiveDuration = 0;
220                         }
221                     }
222                     mActiveProcState = mProcState;
223                 }
224             } else if (mAssociationState != null) {
225                 Slog.wtf(TAG, "startActive while not tracking: " + this);
226             }
227             if (mAssociationState != null) {
228                 final SourceState commonSource = getCommonSourceState(true);
229                 if (commonSource != null && startActive) {
230                     commonSource.startActive(now);
231                 }
232             }
233         }
234 
stopActive(long now)235         void stopActive(long now) {
236             boolean stopActive = false;
237             if (mActiveStartUptime != 0) {
238                 if (!mInTrackingList && mAssociationState != null) {
239                     Slog.wtf(TAG, "stopActive while not tracking: " + this);
240                 }
241                 mActiveNesting--;
242                 final long addedDuration = now - mActiveStartUptime;
243                 mActiveStartUptime = mAssociationState != null || mActiveNesting == 0 ? 0 : now;
244                 stopActive = mActiveStartUptime == 0;
245                 if (mActiveDurations != null) {
246                     mActiveDurations.addDuration(mActiveProcState, addedDuration);
247                 } else {
248                     mActiveDuration += addedDuration;
249                 }
250                 if (mAssociationState != null) {
251                     mAssociationState.mTotalActiveNesting--;
252                     if (mAssociationState.mTotalActiveNesting == 0) {
253                         mAssociationState.mTotalActiveDuration += now
254                                 - mAssociationState.mTotalActiveStartUptime;
255                         mAssociationState.mTotalActiveStartUptime = 0;
256                         if (VALIDATE_TIMES) {
257                             if (mActiveDuration > mAssociationState.mTotalActiveDuration) {
258                                 RuntimeException ex = new RuntimeException();
259                                 ex.fillInStackTrace();
260                                 Slog.w(TAG, "Source act duration " + mActiveDurations
261                                         + " exceeds total " + mAssociationState.mTotalActiveDuration
262                                         + " in procstate " + mActiveProcState + " in source "
263                                         + mKey.mProcess + " to assoc "
264                                         + mAssociationState.mName, ex);
265                             }
266 
267                         }
268                     }
269                 }
270             }
271 
272             if (mAssociationState != null) {
273                 final SourceState commonSource = getCommonSourceState(false);
274                 if (commonSource != null && stopActive) {
275                     commonSource.stopActive(now);
276                 }
277             }
278         }
279 
stopActiveIfNecessary(int curSeq, long now)280         boolean stopActiveIfNecessary(int curSeq, long now) {
281             if (mProcStateSeq != curSeq || mProcState >= ProcessStats.STATE_HOME) {
282                 // If this association did not get touched the last time we computed
283                 // process states, or its state ended up down in cached, then we no
284                 // longer have a reason to track it at all.
285                 stopActive(now);
286                 stopTrackingProcState();
287                 return true;
288             }
289             return false;
290         }
291 
stopTrackingProcState()292         private void stopTrackingProcState() {
293             mInTrackingList = false;
294             mProcState = ProcessStats.STATE_NOTHING;
295             if (mAssociationState != null) {
296                 final SourceState commonSource = getCommonSourceState(false);
297                 if (commonSource != null) {
298                     commonSource.stopTrackingProcState();
299                 }
300             }
301         }
302 
isInUse()303         boolean isInUse() {
304             return mNesting > 0;
305         }
306 
resetSafely(long now)307         void resetSafely(long now) {
308             if (isInUse()) {
309                 mCount = 1;
310                 mStartUptime = now;
311                 mDuration = 0;
312                 if (mActiveStartUptime > 0) {
313                     mActiveCount = 1;
314                     mActiveStartUptime = now;
315                 } else {
316                     mActiveCount = 0;
317                 }
318                 mActiveDuration = 0;
319                 mActiveDurations = null;
320             }
321             // We're actually resetting the common sources in process state already,
322             // resetting it here too in case they're out of sync.
323             if (mAssociationState != null) {
324                 final SourceState commonSource = getCommonSourceState(false);
325                 if (commonSource != null) {
326                     commonSource.resetSafely(now);
327                     mCommonSourceState = null;
328                 }
329             }
330         }
331 
commitStateTime(long nowUptime)332         void commitStateTime(long nowUptime) {
333             if (mNesting > 0) {
334                 mDuration += nowUptime - mStartUptime;
335                 mStartUptime = nowUptime;
336             }
337             if (mActiveStartUptime > 0) {
338                 final long addedDuration = nowUptime - mActiveStartUptime;
339                 mActiveStartUptime = nowUptime;
340                 if (mActiveDurations != null) {
341                     mActiveDurations.addDuration(mActiveProcState, addedDuration);
342                 } else {
343                     mActiveDuration += addedDuration;
344                 }
345             }
346         }
347 
makeDurations()348         void makeDurations() {
349             mActiveDurations = new DurationsTable(mProcessStats.mTableData);
350         }
351 
stopTracking(long now)352         private void stopTracking(long now) {
353             if (mAssociationState != null) {
354                 mAssociationState.mTotalNesting--;
355                 if (mAssociationState.mTotalNesting == 0) {
356                     mAssociationState.mTotalDuration += now
357                             - mAssociationState.mTotalStartUptime;
358                 }
359             }
360             stopActive(now);
361             if (mInTrackingList) {
362                 mInTrackingList = false;
363                 mProcState = ProcessStats.STATE_NOTHING;
364                 if (mAssociationState != null) {
365                     // Do a manual search for where to remove, since these objects will typically
366                     // be towards the end of the array.
367                     final ArrayList<SourceState> list = mProcessStats.mTrackingAssociations;
368                     for (int i = list.size() - 1; i >= 0; i--) {
369                         if (list.get(i) == this) {
370                             list.remove(i);
371                             return;
372                         }
373                     }
374                     Slog.wtf(TAG, "Stop tracking didn't find in tracking list: " + this);
375                 }
376             }
377         }
378 
add(SourceState otherSrc)379         void add(SourceState otherSrc) {
380             mCount += otherSrc.mCount;
381             mDuration += otherSrc.mDuration;
382             mActiveCount += otherSrc.mActiveCount;
383             if (otherSrc.mActiveDuration != 0 || otherSrc.mActiveDurations != null) {
384                 // Only need to do anything if the other one has some duration data.
385                 if (mActiveDurations != null) {
386                     // If the target already has multiple durations, just add in whatever
387                     // we have in the other.
388                     if (otherSrc.mActiveDurations != null) {
389                         mActiveDurations.addDurations(otherSrc.mActiveDurations);
390                     } else {
391                         mActiveDurations.addDuration(otherSrc.mActiveProcState,
392                                 otherSrc.mActiveDuration);
393                     }
394                 } else if (otherSrc.mActiveDurations != null) {
395                     // The other one has multiple durations, but we don't.  Expand to
396                     // multiple durations and copy over.
397                     makeDurations();
398                     mActiveDurations.addDurations(otherSrc.mActiveDurations);
399                     if (mActiveDuration != 0) {
400                         mActiveDurations.addDuration(mActiveProcState, mActiveDuration);
401                         mActiveDuration = 0;
402                         mActiveProcState = ProcessStats.STATE_NOTHING;
403                     }
404                 } else if (mActiveDuration != 0) {
405                     // Both have a single inline duration...  we can either add them together,
406                     // or need to expand to multiple durations.
407                     if (mActiveProcState == otherSrc.mActiveProcState) {
408                         mActiveDuration += otherSrc.mActiveDuration;
409                     } else {
410                         // The two have durations with different proc states, need to turn
411                         // in to multiple durations.
412                         makeDurations();
413                         mActiveDurations.addDuration(mActiveProcState, mActiveDuration);
414                         mActiveDurations.addDuration(otherSrc.mActiveProcState,
415                                 otherSrc.mActiveDuration);
416                         mActiveDuration = 0;
417                         mActiveProcState = ProcessStats.STATE_NOTHING;
418                     }
419                 } else {
420                     // The other one has a duration, and we know the target doesn't.  Copy over.
421                     mActiveProcState = otherSrc.mActiveProcState;
422                     mActiveDuration = otherSrc.mActiveDuration;
423                 }
424             }
425         }
426 
427         @Override
writeToParcel(Parcel out, int flags)428         public void writeToParcel(Parcel out, int flags) {
429             out.writeInt(mCount);
430             out.writeLong(mDuration);
431             out.writeInt(mActiveCount);
432             if (mActiveDurations != null) {
433                 out.writeInt(1);
434                 mActiveDurations.writeToParcel(out);
435             } else {
436                 out.writeInt(0);
437                 out.writeInt(mActiveProcState);
438                 out.writeLong(mActiveDuration);
439             }
440         }
441 
442         @Override
describeContents()443         public int describeContents() {
444             return 0;
445         }
446 
readFromParcel(Parcel in)447         String readFromParcel(Parcel in) {
448             mCount = in.readInt();
449             mDuration = in.readLong();
450             mActiveCount = in.readInt();
451             if (in.readInt() != 0) {
452                 makeDurations();
453                 if (!mActiveDurations.readFromParcel(in)) {
454                     return "Duration table corrupt: " + mKey + " <- " + toString();
455                 }
456             } else {
457                 mActiveProcState = in.readInt();
458                 mActiveDuration = in.readLong();
459             }
460             return null;
461         }
462 
463         @Override
toString()464         public String toString() {
465             StringBuilder sb = new StringBuilder(64);
466             sb.append("SourceState{").append(Integer.toHexString(System.identityHashCode(this)))
467                     .append(" ").append(mKey.mProcess).append("/").append(mKey.mUid);
468             if (mProcState != ProcessStats.STATE_NOTHING) {
469                 sb.append(" ").append(DumpUtils.STATE_NAMES[mProcState]).append(" #")
470                         .append(mProcStateSeq);
471             }
472             sb.append("}");
473             return sb.toString();
474         }
475     }
476 
477     static final class SourceDumpContainer {
478         public final SourceState mState;
479         public long mTotalTime;
480         public long mActiveTime;
481 
SourceDumpContainer(SourceState state)482         public SourceDumpContainer(SourceState state) {
483             mState = state;
484         }
485     }
486 
487     public static final class SourceKey {
488         /**
489          * UID, consider this final.  Not final just to avoid a temporary object during lookup.
490          */
491         int mUid;
492 
493         /**
494          * Process name, consider this final.  Not final just to avoid a temporary object during
495          * lookup.
496          */
497         String mProcess;
498 
499         /**
500          * Optional package name, or null; consider this final.  Not final just to avoid a
501          * temporary object during lookup.
502          */
503         @Nullable String mPackage;
504 
SourceKey(int uid, String process, String pkg)505         SourceKey(int uid, String process, String pkg) {
506             mUid = uid;
507             mProcess = process;
508             mPackage = pkg;
509         }
510 
SourceKey(ProcessStats stats, Parcel in, int parcelVersion)511         SourceKey(ProcessStats stats, Parcel in, int parcelVersion) {
512             mUid = in.readInt();
513             mProcess = stats.readCommonString(in, parcelVersion);
514             mPackage = stats.readCommonString(in, parcelVersion);
515         }
516 
writeToParcel(ProcessStats stats, Parcel out)517         void writeToParcel(ProcessStats stats, Parcel out) {
518             out.writeInt(mUid);
519             stats.writeCommonString(out, mProcess);
520             stats.writeCommonString(out, mPackage);
521         }
522 
equals(Object o)523         public boolean equals(Object o) {
524             if (!(o instanceof SourceKey)) {
525                 return false;
526             }
527             SourceKey s = (SourceKey) o;
528             return s.mUid == mUid && Objects.equals(s.mProcess, mProcess)
529                     && Objects.equals(s.mPackage, mPackage);
530         }
531 
532         @Override
hashCode()533         public int hashCode() {
534             return Integer.hashCode(mUid) ^ (mProcess == null ? 0 : mProcess.hashCode())
535                     ^ (mPackage == null ? 0 : (mPackage.hashCode() * 33));
536         }
537 
538         @Override
toString()539         public String toString() {
540             StringBuilder sb = new StringBuilder(64);
541             sb.append("SourceKey{");
542             UserHandle.formatUid(sb, mUid);
543             sb.append(' ');
544             sb.append(mProcess);
545             sb.append(' ');
546             sb.append(mPackage);
547             sb.append('}');
548             return sb.toString();
549         }
550     }
551 
552     /**
553      * All known sources for this target component...  uid -> process name -> source state.
554      */
555     final ArrayMap<SourceKey, SourceState> mSources = new ArrayMap<>();
556 
557     private static final SourceKey sTmpSourceKey = new SourceKey(0, null, null);
558 
559     private ProcessState mProc;
560 
AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState, String name, String processName, ProcessState proc)561     public AssociationState(ProcessStats processStats, ProcessStats.PackageState packageState,
562             String name, String processName, ProcessState proc) {
563         mProcessStats = processStats;
564         mPackageState = packageState;
565         mName = name;
566         mProcessName = processName;
567         mProc = proc;
568     }
569 
getUid()570     public int getUid() {
571         return mPackageState.mUid;
572     }
573 
getPackage()574     public String getPackage() {
575         return mPackageState.mPackageName;
576     }
577 
getProcessName()578     public String getProcessName() {
579         return mProcessName;
580     }
581 
getName()582     public String getName() {
583         return mName;
584     }
585 
getProcess()586     public ProcessState getProcess() {
587         return mProc;
588     }
589 
setProcess(ProcessState proc)590     public void setProcess(ProcessState proc) {
591         mProc = proc;
592     }
593 
getTotalDuration(long now)594     public long getTotalDuration(long now) {
595         return mTotalDuration
596                 + (mTotalNesting > 0 ? (now - mTotalStartUptime) : 0);
597     }
598 
getActiveDuration(long now)599     public long getActiveDuration(long now) {
600         return mTotalActiveDuration
601                 + (mTotalActiveNesting > 0 ? (now - mTotalActiveStartUptime) : 0);
602     }
603 
startSource(int uid, String processName, String packageName)604     public SourceState startSource(int uid, String processName, String packageName) {
605         SourceState src;
606         synchronized (sTmpSourceKey) {
607             sTmpSourceKey.mUid = uid;
608             sTmpSourceKey.mProcess = processName;
609             sTmpSourceKey.mPackage = packageName;
610             src = mSources.get(sTmpSourceKey);
611         }
612         if (src == null) {
613             SourceKey key = new SourceKey(uid, processName, packageName);
614             src = new SourceState(mProcessStats, this, mProc, key);
615             mSources.put(key, src);
616         }
617         final long now = src.start();
618         if (now > 0) {
619             mTotalNesting++;
620             if (mTotalNesting == 1) {
621                 mTotalCount++;
622                 mTotalStartUptime = now;
623             }
624         }
625         return src;
626     }
627 
add(AssociationState other)628     public void add(AssociationState other) {
629         mTotalCount += other.mTotalCount;
630         final long origDuration = mTotalDuration;
631         mTotalDuration += other.mTotalDuration;
632         mTotalActiveCount += other.mTotalActiveCount;
633         mTotalActiveDuration += other.mTotalActiveDuration;
634         for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
635             final SourceKey key = other.mSources.keyAt(isrc);
636             final SourceState otherSrc = other.mSources.valueAt(isrc);
637             SourceState mySrc = mSources.get(key);
638             boolean newSrc = false;
639             if (mySrc == null) {
640                 mySrc = new SourceState(mProcessStats, this, mProc, key);
641                 mSources.put(key, mySrc);
642                 newSrc = true;
643             }
644             if (VALIDATE_TIMES) {
645                 Slog.w(TAG, "Adding tot duration " + mySrc.mDuration + "+"
646                         + otherSrc.mDuration
647                         + (newSrc ? " (new)" : " (old)") + " (total "
648                         + origDuration + "+" + other.mTotalDuration + ") in source "
649                         + mySrc.mKey.mProcess + " to assoc " + mName);
650                 if ((mySrc.mDuration + otherSrc.mDuration) > mTotalDuration) {
651                     RuntimeException ex = new RuntimeException();
652                     ex.fillInStackTrace();
653                     Slog.w(TAG, "Source tot duration " + mySrc.mDuration + "+"
654                             + otherSrc.mDuration
655                             + (newSrc ? " (new)" : " (old)") + " exceeds total "
656                             + origDuration + "+" + other.mTotalDuration + " in source "
657                             + mySrc.mKey.mProcess + " to assoc " + mName, ex);
658                 }
659                 if (mySrc.mActiveDurations == null && otherSrc.mActiveDurations == null) {
660                     Slog.w(TAG, "Adding act duration " + mySrc.mActiveDuration
661                             + "+" + otherSrc.mActiveDuration
662                             + (newSrc ? " (new)" : " (old)") + " (total "
663                             + origDuration + "+" + other.mTotalDuration + ") in source "
664                             + mySrc.mKey.mProcess + " to assoc " + mName);
665                     if ((mySrc.mActiveDuration + otherSrc.mActiveDuration) > mTotalDuration) {
666                         RuntimeException ex = new RuntimeException();
667                         ex.fillInStackTrace();
668                         Slog.w(TAG, "Source act duration " + mySrc.mActiveDuration + "+"
669                                 + otherSrc.mActiveDuration
670                                 + (newSrc ? " (new)" : " (old)") + " exceeds total "
671                                 + origDuration + "+" + other.mTotalDuration + " in source "
672                                 + mySrc.mKey.mProcess + " to assoc " + mName, ex);
673                     }
674                 }
675             }
676             mySrc.add(otherSrc);
677         }
678     }
679 
isInUse()680     public boolean isInUse() {
681         return mTotalNesting > 0;
682     }
683 
resetSafely(long now)684     public void resetSafely(long now) {
685         if (!isInUse()) {
686             mSources.clear();
687             mTotalCount = mTotalActiveCount = 0;
688         } else {
689             // We have some active sources...  clear out everything but those.
690             for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
691                 SourceState src = mSources.valueAt(isrc);
692                 if (src.isInUse()) {
693                     src.resetSafely(now);
694                 } else {
695                     mSources.removeAt(isrc);
696                 }
697             }
698             mTotalCount = 1;
699             mTotalStartUptime = now;
700             if (mTotalActiveNesting > 0) {
701                 mTotalActiveCount = 1;
702                 mTotalActiveStartUptime = now;
703             } else {
704                 mTotalActiveCount = 0;
705             }
706         }
707         mTotalDuration = mTotalActiveDuration = 0;
708     }
709 
writeToParcel(ProcessStats stats, Parcel out, long nowUptime)710     public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
711         out.writeInt(mTotalCount);
712         out.writeLong(mTotalDuration);
713         out.writeInt(mTotalActiveCount);
714         out.writeLong(mTotalActiveDuration);
715         final int NSRC = mSources.size();
716         out.writeInt(NSRC);
717         for (int isrc = 0; isrc < NSRC; isrc++) {
718             final SourceKey key = mSources.keyAt(isrc);
719             final SourceState src = mSources.valueAt(isrc);
720             key.writeToParcel(stats, out);
721             src.writeToParcel(out, 0);
722         }
723     }
724 
725     /**
726      * Returns non-null if all else fine, else a String that describes the error that
727      * caused it to fail.
728      */
readFromParcel(ProcessStats stats, Parcel in, int parcelVersion)729     public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
730         mTotalCount = in.readInt();
731         mTotalDuration = in.readLong();
732         mTotalActiveCount = in.readInt();
733         mTotalActiveDuration = in.readLong();
734         final int NSRC = in.readInt();
735         if (NSRC < 0 || NSRC > 100000) {
736             return "Association with bad src count: " + NSRC;
737         }
738         for (int isrc = 0; isrc < NSRC; isrc++) {
739             final SourceKey key = new SourceKey(stats, in, parcelVersion);
740             final SourceState src = new SourceState(mProcessStats, this, mProc, key);
741             final String errMsg = src.readFromParcel(in);
742             if (errMsg != null) {
743                 return errMsg;
744             }
745             if (VALIDATE_TIMES) {
746                 if (src.mDuration > mTotalDuration) {
747                     RuntimeException ex = new RuntimeException();
748                     ex.fillInStackTrace();
749                     Slog.w(TAG, "Reading tot duration " + src.mDuration
750                             + " exceeds total " + mTotalDuration + " in source "
751                             + src.mKey.mProcess + " to assoc " + mName, ex);
752                 }
753                 if (src.mActiveDurations == null && src.mActiveDuration > mTotalDuration) {
754                     RuntimeException ex = new RuntimeException();
755                     ex.fillInStackTrace();
756                     Slog.w(TAG, "Reading act duration " + src.mActiveDuration
757                             + " exceeds total " + mTotalDuration + " in source "
758                             + src.mKey.mProcess + " to assoc " + mName, ex);
759                 }
760             }
761             mSources.put(key, src);
762         }
763         return null;
764     }
765 
commitStateTime(long nowUptime)766     public void commitStateTime(long nowUptime) {
767         if (isInUse()) {
768             for (int isrc = mSources.size() - 1; isrc >= 0; isrc--) {
769                 SourceState src = mSources.valueAt(isrc);
770                 src.commitStateTime(nowUptime);
771             }
772             if (mTotalNesting > 0) {
773                 mTotalDuration += nowUptime - mTotalStartUptime;
774                 mTotalStartUptime = nowUptime;
775             }
776             if (mTotalActiveNesting > 0) {
777                 mTotalActiveDuration += nowUptime - mTotalActiveStartUptime;
778                 mTotalActiveStartUptime = nowUptime;
779             }
780         }
781     }
782 
hasProcessOrPackage(String procName)783     public boolean hasProcessOrPackage(String procName) {
784         if (mProcessName.equals(procName)) {
785             return true;
786         }
787         final int NSRC = mSources.size();
788         for (int isrc = 0; isrc < NSRC; isrc++) {
789             final SourceKey key = mSources.keyAt(isrc);
790             if (procName.equals(key.mProcess) || procName.equals(key.mPackage)) {
791                 return true;
792             }
793         }
794         return false;
795     }
796 
797     static final Comparator<Pair<SourceKey, SourceDumpContainer>> ASSOCIATION_COMPARATOR =
798             (o1, o2) -> {
799         if (o1.second.mActiveTime != o2.second.mActiveTime) {
800             return o1.second.mActiveTime > o2.second.mActiveTime ? -1 : 1;
801         }
802         if (o1.second.mTotalTime != o2.second.mTotalTime) {
803             return o1.second.mTotalTime > o2.second.mTotalTime ? -1 : 1;
804         }
805         if (o1.first.mUid != o2.first.mUid) {
806             return o1.first.mUid < o2.first.mUid ? -1 : 1;
807         }
808         if (o1.first.mProcess != o2.first.mProcess) {
809             int diff = o1.first.mProcess.compareTo(o2.first.mProcess);
810             if (diff != 0) {
811                 return diff;
812             }
813         }
814         return 0;
815     };
816 
createSortedAssociations(long now, long totalTime, ArrayMap<SourceKey, SourceState> inSources)817     static ArrayList<Pair<SourceKey, SourceDumpContainer>> createSortedAssociations(long now,
818             long totalTime, ArrayMap<SourceKey, SourceState> inSources) {
819         final int numOfSources = inSources.size();
820         ArrayList<Pair<SourceKey, SourceDumpContainer>> sources = new ArrayList<>(numOfSources);
821         for (int isrc = 0; isrc < numOfSources; isrc++) {
822             final SourceState src = inSources.valueAt(isrc);
823             final SourceDumpContainer cont = new SourceDumpContainer(src);
824             long duration = src.mDuration;
825             if (src.mNesting > 0) {
826                 duration += now - src.mStartUptime;
827             }
828             cont.mTotalTime = duration;
829             cont.mActiveTime = dumpTime(null, null, src, totalTime, now, false, false);
830             if (cont.mActiveTime < 0) {
831                 cont.mActiveTime = -cont.mActiveTime;
832             }
833             sources.add(new Pair<>(inSources.keyAt(isrc), cont));
834         }
835         Collections.sort(sources, ASSOCIATION_COMPARATOR);
836         return sources;
837     }
838 
dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix, ArrayList<Pair<SourceKey, SourceDumpContainer>> sources, long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll)839     public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
840             ArrayList<Pair<SourceKey, SourceDumpContainer>> sources, long now, long totalTime,
841             String reqPackage, boolean dumpDetails, boolean dumpAll) {
842         final String prefixInnerInner = prefixInner + "     ";
843         long totalDuration = mTotalActiveDuration;
844         if (mTotalActiveNesting > 0) {
845             totalDuration += now - mTotalActiveStartUptime;
846         }
847         if (totalDuration > 0 || mTotalActiveCount != 0) {
848             pw.print(prefix);
849             pw.print("Active count ");
850             pw.print(mTotalActiveCount);
851             if (dumpAll) {
852                 pw.print(": ");
853                 TimeUtils.formatDuration(totalDuration, pw);
854                 pw.print(" / ");
855             } else {
856                 pw.print(": time ");
857             }
858             DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
859             pw.println();
860         }
861         if (dumpAll && mTotalActiveNesting != 0) {
862             pw.print(prefix);
863             pw.print("mTotalActiveNesting=");
864             pw.print(mTotalActiveNesting);
865             pw.print(" mTotalActiveStartUptime=");
866             TimeUtils.formatDuration(mTotalActiveStartUptime, now, pw);
867             pw.println();
868         }
869         totalDuration = mTotalDuration;
870         if (mTotalNesting > 0) {
871             totalDuration += now - mTotalStartUptime;
872         }
873         if (totalDuration > 0 || mTotalCount != 0) {
874             pw.print(prefix);
875             pw.print("Total count ");
876             pw.print(mTotalCount);
877             if (dumpAll) {
878                 pw.print(": ");
879                 TimeUtils.formatDuration(totalDuration, pw);
880                 pw.print(" / ");
881             } else {
882                 pw.print(": time ");
883             }
884             DumpUtils.printPercent(pw, (double) totalDuration / (double) totalTime);
885             pw.println();
886         }
887         if (dumpAll && mTotalNesting != 0) {
888             pw.print(prefix);
889             pw.print("mTotalNesting=");
890             pw.print(mTotalNesting);
891             pw.print(" mTotalStartUptime=");
892             TimeUtils.formatDuration(mTotalStartUptime, now, pw);
893             pw.println();
894         }
895 
896         dumpSources(pw, prefix, prefixInner, prefixInnerInner, sources, now, totalTime,
897                 reqPackage, dumpDetails, dumpAll);
898     }
899 
dumpSources(PrintWriter pw, String prefix, String prefixInner, String prefixInnerInner, ArrayList<Pair<SourceKey, SourceDumpContainer>> sources, long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll)900     static void dumpSources(PrintWriter pw, String prefix, String prefixInner,
901             String prefixInnerInner, ArrayList<Pair<SourceKey, SourceDumpContainer>> sources,
902             long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll) {
903         final int NSRC = sources.size();
904         for (int isrc = 0; isrc < NSRC; isrc++) {
905             final SourceKey key = sources.get(isrc).first;
906             final SourceDumpContainer cont = sources.get(isrc).second;
907             final SourceState src = cont.mState;
908             pw.print(prefix);
909             pw.print("<- ");
910             pw.print(key.mProcess);
911             pw.print("/");
912             UserHandle.formatUid(pw, key.mUid);
913             if (key.mPackage != null) {
914                 pw.print(" (");
915                 pw.print(key.mPackage);
916                 pw.print(")");
917             }
918             // If we are skipping this one, we still print the first line just to give
919             // context for the others (so it is clear the total times for the overall
920             // association come from other sources whose times are not shown).
921             if (reqPackage != null && !reqPackage.equals(key.mProcess)
922                     && !reqPackage.equals(key.mPackage)) {
923                 pw.println();
924                 continue;
925             }
926             pw.println(":");
927             if (src.mActiveCount != 0 || src.mActiveDurations != null || src.mActiveDuration != 0
928                     || src.mActiveStartUptime != 0) {
929                 pw.print(prefixInner);
930                 pw.print("   Active count ");
931                 pw.print(src.mActiveCount);
932                 if (dumpDetails) {
933                     if (dumpAll) {
934                         if (src.mActiveDurations != null) {
935                             pw.print(" (multi-state)");
936                         } else if (src.mActiveProcState >= ProcessStats.STATE_PERSISTENT) {
937                             pw.print(" (");
938                             pw.print(DumpUtils.STATE_NAMES[src.mActiveProcState]);
939                             pw.print(")");
940                         } else {
941                             pw.print(" (*UNKNOWN STATE*)");
942                         }
943                     }
944                     if (dumpAll) {
945                         pw.print(": ");
946                         TimeUtils.formatDuration(cont.mActiveTime, pw);
947                         pw.print(" / ");
948                     } else {
949                         pw.print(": time ");
950                     }
951                     DumpUtils.printPercent(pw, (double) cont.mActiveTime / (double) totalTime);
952                     if (src.mActiveStartUptime != 0) {
953                         pw.print(" (running)");
954                     }
955                     pw.println();
956                     if (src.mActiveDurations != null) {
957                         dumpTime(pw, prefixInnerInner, src, totalTime, now, dumpDetails, dumpAll);
958                     }
959                 } else {
960                     pw.print(": ");
961                     dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
962                 }
963             }
964             pw.print(prefixInner);
965             pw.print("   Total count ");
966             pw.print(src.mCount);
967             if (dumpAll) {
968                 pw.print(": ");
969                 TimeUtils.formatDuration(cont.mTotalTime, pw);
970                 pw.print(" / ");
971             } else {
972                 pw.print(": time ");
973             }
974             DumpUtils.printPercent(pw, (double) cont.mTotalTime / (double) totalTime);
975             if (src.mNesting > 0) {
976                 pw.print(" (running");
977                 if (dumpAll) {
978                     pw.print(" nest=");
979                     pw.print(src.mNesting);
980                 }
981                 if (src.mProcState != ProcessStats.STATE_NOTHING) {
982                     pw.print(" / ");
983                     pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
984                     pw.print(" #");
985                     pw.print(src.mProcStateSeq);
986                 }
987                 pw.print(")");
988             }
989             pw.println();
990             if (dumpAll) {
991                 if (src.mInTrackingList) {
992                     pw.print(prefixInner);
993                     pw.print("   mInTrackingList=");
994                     pw.println(src.mInTrackingList);
995                 }
996                 if (src.mProcState != ProcessStats.STATE_NOTHING) {
997                     pw.print(prefixInner);
998                     pw.print("   mProcState=");
999                     pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
1000                     pw.print(" mProcStateSeq=");
1001                     pw.println(src.mProcStateSeq);
1002                 }
1003             }
1004         }
1005     }
1006 
dumpActiveDurationSummary(PrintWriter pw, final SourceState src, long totalTime, long now, boolean dumpAll)1007     static void dumpActiveDurationSummary(PrintWriter pw, final SourceState src, long totalTime,
1008             long now, boolean dumpAll) {
1009         long duration = dumpTime(null, null, src, totalTime, now, false, false);
1010         final boolean isRunning = duration < 0;
1011         if (isRunning) {
1012             duration = -duration;
1013         }
1014         if (dumpAll) {
1015             TimeUtils.formatDuration(duration, pw);
1016             pw.print(" / ");
1017         } else {
1018             pw.print("time ");
1019         }
1020         DumpUtils.printPercent(pw, (double) duration / (double) totalTime);
1021         if (src.mActiveStartUptime > 0) {
1022             pw.print(" (running)");
1023         }
1024         pw.println();
1025     }
1026 
dumpTime(PrintWriter pw, String prefix, final SourceState src, long overallTime, long now, boolean dumpDetails, boolean dumpAll)1027     static long dumpTime(PrintWriter pw, String prefix, final SourceState src, long overallTime,
1028             long now, boolean dumpDetails, boolean dumpAll) {
1029         long totalTime = 0;
1030         boolean isRunning = false;
1031         for (int iprocstate = 0; iprocstate < ProcessStats.STATE_COUNT; iprocstate++) {
1032             long time;
1033             if (src.mActiveDurations != null) {
1034                 time = src.mActiveDurations.getValueForId((byte) iprocstate);
1035             } else {
1036                 time = src.mActiveProcState == iprocstate ? src.mActiveDuration : 0;
1037             }
1038             final String running;
1039             if (src.mActiveStartUptime != 0 && src.mActiveProcState == iprocstate) {
1040                 running = " (running)";
1041                 isRunning = true;
1042                 time += now - src.mActiveStartUptime;
1043             } else {
1044                 running = null;
1045             }
1046             if (time != 0) {
1047                 if (pw != null) {
1048                     pw.print(prefix);
1049                     pw.print(DumpUtils.STATE_LABELS[iprocstate]);
1050                     pw.print(": ");
1051                     if (dumpAll) {
1052                         TimeUtils.formatDuration(time, pw);
1053                         pw.print(" / ");
1054                     } else {
1055                         pw.print("time ");
1056                     }
1057                     DumpUtils.printPercent(pw, (double) time / (double) overallTime);
1058                     if (running != null) {
1059                         pw.print(running);
1060                     }
1061                     pw.println();
1062                 }
1063                 totalTime += time;
1064             }
1065         }
1066         return isRunning ? -totalTime : totalTime;
1067     }
1068 
dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers, String associationName, long now)1069     public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
1070             String associationName, long now) {
1071         final int NSRC = mSources.size();
1072         for (int isrc = 0; isrc < NSRC; isrc++) {
1073             final SourceKey key = mSources.keyAt(isrc);
1074             final SourceState src = mSources.valueAt(isrc);
1075             pw.print("pkgasc");
1076             pw.print(",");
1077             pw.print(pkgName);
1078             pw.print(",");
1079             pw.print(uid);
1080             pw.print(",");
1081             pw.print(vers);
1082             pw.print(",");
1083             pw.print(associationName);
1084             pw.print(",");
1085             pw.print(key.mProcess);
1086             pw.print(",");
1087             pw.print(key.mUid);
1088             pw.print(",");
1089             pw.print(src.mCount);
1090             long duration = src.mDuration;
1091             if (src.mNesting > 0) {
1092                 duration += now - src.mStartUptime;
1093             }
1094             pw.print(",");
1095             pw.print(duration);
1096             pw.print(",");
1097             pw.print(src.mActiveCount);
1098             final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
1099             if (src.mActiveDurations != null) {
1100                 final int N = src.mActiveDurations.getKeyCount();
1101                 for (int i=0; i<N; i++) {
1102                     final int dkey = src.mActiveDurations.getKeyAt(i);
1103                     duration = src.mActiveDurations.getValue(dkey);
1104                     if (dkey == src.mActiveProcState) {
1105                         duration += timeNow;
1106                     }
1107                     final int procState = SparseMappingTable.getIdFromKey(dkey);
1108                     pw.print(",");
1109                     DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS,  procState, 1);
1110                     pw.print(':');
1111                     pw.print(duration);
1112                 }
1113             } else {
1114                 duration = src.mActiveDuration + timeNow;
1115                 if (duration != 0) {
1116                     pw.print(",");
1117                     DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS,  src.mActiveProcState, 1);
1118                     pw.print(':');
1119                     pw.print(duration);
1120                 }
1121             }
1122             pw.println();
1123         }
1124     }
1125 
dumpDebug(ProtoOutputStream proto, long fieldId, long now)1126     public void dumpDebug(ProtoOutputStream proto, long fieldId, long now) {
1127         final long token = proto.start(fieldId);
1128 
1129         proto.write(PackageAssociationProcessStatsProto.COMPONENT_NAME, mName);
1130 
1131         proto.write(PackageAssociationProcessStatsProto.TOTAL_COUNT, mTotalCount);
1132         proto.write(PackageAssociationProcessStatsProto.TOTAL_DURATION_MS, getTotalDuration(now));
1133         if (mTotalActiveCount != 0) {
1134             proto.write(PackageAssociationProcessStatsProto.ACTIVE_COUNT, mTotalActiveCount);
1135             proto.write(PackageAssociationProcessStatsProto.ACTIVE_DURATION_MS,
1136                     getActiveDuration(now));
1137         }
1138 
1139         final int NSRC = mSources.size();
1140         for (int isrc = 0; isrc < NSRC; isrc++) {
1141             final SourceKey key = mSources.keyAt(isrc);
1142             final SourceState src = mSources.valueAt(isrc);
1143             final long sourceToken = proto.start(PackageAssociationProcessStatsProto.SOURCES);
1144             proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_NAME, key.mProcess);
1145             proto.write(PackageAssociationSourceProcessStatsProto.PACKAGE_NAME, key.mPackage);
1146             proto.write(PackageAssociationSourceProcessStatsProto.PROCESS_UID, key.mUid);
1147             proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_COUNT, src.mCount);
1148             long duration = src.mDuration;
1149             if (src.mNesting > 0) {
1150                 duration += now - src.mStartUptime;
1151             }
1152             proto.write(PackageAssociationSourceProcessStatsProto.TOTAL_DURATION_MS, duration);
1153             if (src.mActiveCount != 0) {
1154                 proto.write(PackageAssociationSourceProcessStatsProto.ACTIVE_COUNT,
1155                         src.mActiveCount);
1156             }
1157             final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
1158             if (src.mActiveDurations != null) {
1159                 final int N = src.mActiveDurations.getKeyCount();
1160                 for (int i=0; i<N; i++) {
1161                     final int dkey = src.mActiveDurations.getKeyAt(i);
1162                     duration = src.mActiveDurations.getValue(dkey);
1163                     if (dkey == src.mActiveProcState) {
1164                         duration += timeNow;
1165                     }
1166                     final int procState = SparseMappingTable.getIdFromKey(dkey);
1167                     final long stateToken = proto.start(
1168                             PackageAssociationSourceProcessStatsProto.ACTIVE_STATE_STATS);
1169                     DumpUtils.printProto(proto,
1170                             PackageAssociationSourceProcessStatsProto.StateStats.PROCESS_STATE,
1171                             DumpUtils.STATE_PROTO_ENUMS, procState, 1);
1172                     proto.write(PackageAssociationSourceProcessStatsProto.StateStats.DURATION_MS,
1173                             duration);
1174                     proto.end(stateToken);
1175                 }
1176             } else {
1177                 duration = src.mActiveDuration + timeNow;
1178                 if (duration != 0) {
1179                     final long stateToken = proto.start(
1180                             PackageAssociationSourceProcessStatsProto.ACTIVE_STATE_STATS);
1181                     DumpUtils.printProto(proto,
1182                             PackageAssociationSourceProcessStatsProto.StateStats.PROCESS_STATE,
1183                             DumpUtils.STATE_PROTO_ENUMS, src.mActiveProcState, 1);
1184                     proto.write(PackageAssociationSourceProcessStatsProto.StateStats.DURATION_MS,
1185                             duration);
1186                     proto.end(stateToken);
1187                 }
1188             }
1189             proto.end(sourceToken);
1190         }
1191 
1192         proto.end(token);
1193     }
1194 
toString()1195     public String toString() {
1196         return "AssociationState{" + Integer.toHexString(System.identityHashCode(this))
1197                 + " " + mName + " pkg=" + mPackageState.mPackageName + " proc="
1198                 + Integer.toHexString(System.identityHashCode(mProc)) + "}";
1199     }
1200 }
1201