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