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