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