1 /* 2 * Copyright (C) 2007 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.os; 18 19 import static android.os.Process.*; 20 21 import android.os.FileUtils; 22 import android.os.Process; 23 import android.os.StrictMode; 24 import android.os.SystemClock; 25 import android.system.OsConstants; 26 import android.util.Slog; 27 28 import com.android.internal.util.FastPrintWriter; 29 30 import libcore.io.IoUtils; 31 import libcore.io.Libcore; 32 33 import java.io.File; 34 import java.io.FileInputStream; 35 import java.io.PrintWriter; 36 import java.io.StringWriter; 37 import java.text.SimpleDateFormat; 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.Comparator; 41 import java.util.Date; 42 import java.util.StringTokenizer; 43 44 public class ProcessCpuTracker { 45 private static final String TAG = "ProcessCpuTracker"; 46 private static final boolean DEBUG = false; 47 private static final boolean localLOGV = DEBUG || false; 48 49 private static final int[] PROCESS_STATS_FORMAT = new int[] { 50 PROC_SPACE_TERM, 51 PROC_SPACE_TERM|PROC_PARENS, 52 PROC_SPACE_TERM, 53 PROC_SPACE_TERM, 54 PROC_SPACE_TERM, 55 PROC_SPACE_TERM, 56 PROC_SPACE_TERM, 57 PROC_SPACE_TERM, 58 PROC_SPACE_TERM, 59 PROC_SPACE_TERM|PROC_OUT_LONG, // 10: minor faults 60 PROC_SPACE_TERM, 61 PROC_SPACE_TERM|PROC_OUT_LONG, // 12: major faults 62 PROC_SPACE_TERM, 63 PROC_SPACE_TERM|PROC_OUT_LONG, // 14: utime 64 PROC_SPACE_TERM|PROC_OUT_LONG, // 15: stime 65 }; 66 67 static final int PROCESS_STAT_MINOR_FAULTS = 0; 68 static final int PROCESS_STAT_MAJOR_FAULTS = 1; 69 static final int PROCESS_STAT_UTIME = 2; 70 static final int PROCESS_STAT_STIME = 3; 71 72 /** Stores user time and system time in jiffies. */ 73 private final long[] mProcessStatsData = new long[4]; 74 75 /** Stores user time and system time in jiffies. Used for 76 * public API to retrieve CPU use for a process. Must lock while in use. */ 77 private final long[] mSinglePidStatsData = new long[4]; 78 79 private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] { 80 PROC_SPACE_TERM, 81 PROC_SPACE_TERM|PROC_PARENS|PROC_OUT_STRING, // 2: name 82 PROC_SPACE_TERM, 83 PROC_SPACE_TERM, 84 PROC_SPACE_TERM, 85 PROC_SPACE_TERM, 86 PROC_SPACE_TERM, 87 PROC_SPACE_TERM, 88 PROC_SPACE_TERM, 89 PROC_SPACE_TERM|PROC_OUT_LONG, // 10: minor faults 90 PROC_SPACE_TERM, 91 PROC_SPACE_TERM|PROC_OUT_LONG, // 12: major faults 92 PROC_SPACE_TERM, 93 PROC_SPACE_TERM|PROC_OUT_LONG, // 14: utime 94 PROC_SPACE_TERM|PROC_OUT_LONG, // 15: stime 95 PROC_SPACE_TERM, 96 PROC_SPACE_TERM, 97 PROC_SPACE_TERM, 98 PROC_SPACE_TERM, 99 PROC_SPACE_TERM, 100 PROC_SPACE_TERM, 101 PROC_SPACE_TERM, 102 PROC_SPACE_TERM|PROC_OUT_LONG, // 23: vsize 103 }; 104 105 static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1; 106 static final int PROCESS_FULL_STAT_MAJOR_FAULTS = 2; 107 static final int PROCESS_FULL_STAT_UTIME = 3; 108 static final int PROCESS_FULL_STAT_STIME = 4; 109 static final int PROCESS_FULL_STAT_VSIZE = 5; 110 111 private final String[] mProcessFullStatsStringData = new String[6]; 112 private final long[] mProcessFullStatsData = new long[6]; 113 114 private static final int[] SYSTEM_CPU_FORMAT = new int[] { 115 PROC_SPACE_TERM|PROC_COMBINE, 116 PROC_SPACE_TERM|PROC_OUT_LONG, // 1: user time 117 PROC_SPACE_TERM|PROC_OUT_LONG, // 2: nice time 118 PROC_SPACE_TERM|PROC_OUT_LONG, // 3: sys time 119 PROC_SPACE_TERM|PROC_OUT_LONG, // 4: idle time 120 PROC_SPACE_TERM|PROC_OUT_LONG, // 5: iowait time 121 PROC_SPACE_TERM|PROC_OUT_LONG, // 6: irq time 122 PROC_SPACE_TERM|PROC_OUT_LONG // 7: softirq time 123 }; 124 125 private final long[] mSystemCpuData = new long[7]; 126 127 private static final int[] LOAD_AVERAGE_FORMAT = new int[] { 128 PROC_SPACE_TERM|PROC_OUT_FLOAT, // 0: 1 min 129 PROC_SPACE_TERM|PROC_OUT_FLOAT, // 1: 5 mins 130 PROC_SPACE_TERM|PROC_OUT_FLOAT // 2: 15 mins 131 }; 132 133 private final float[] mLoadAverageData = new float[3]; 134 135 private final boolean mIncludeThreads; 136 137 // How long a CPU jiffy is in milliseconds. 138 private final long mJiffyMillis; 139 140 private float mLoad1 = 0; 141 private float mLoad5 = 0; 142 private float mLoad15 = 0; 143 144 // All times are in milliseconds. They are converted from jiffies to milliseconds 145 // when extracted from the kernel. 146 private long mCurrentSampleTime; 147 private long mLastSampleTime; 148 149 private long mCurrentSampleRealTime; 150 private long mLastSampleRealTime; 151 152 private long mCurrentSampleWallTime; 153 private long mLastSampleWallTime; 154 155 private long mBaseUserTime; 156 private long mBaseSystemTime; 157 private long mBaseIoWaitTime; 158 private long mBaseIrqTime; 159 private long mBaseSoftIrqTime; 160 private long mBaseIdleTime; 161 private int mRelUserTime; 162 private int mRelSystemTime; 163 private int mRelIoWaitTime; 164 private int mRelIrqTime; 165 private int mRelSoftIrqTime; 166 private int mRelIdleTime; 167 private boolean mRelStatsAreGood; 168 169 private int[] mCurPids; 170 private int[] mCurThreadPids; 171 172 private final ArrayList<Stats> mProcStats = new ArrayList<Stats>(); 173 private final ArrayList<Stats> mWorkingProcs = new ArrayList<Stats>(); 174 private boolean mWorkingProcsSorted; 175 176 private boolean mFirst = true; 177 178 private byte[] mBuffer = new byte[4096]; 179 180 public static class Stats { 181 public final int pid; 182 public final int uid; 183 final String statFile; 184 final String cmdlineFile; 185 final String threadsDir; 186 final ArrayList<Stats> threadStats; 187 final ArrayList<Stats> workingThreads; 188 189 public BatteryStatsImpl.Uid.Proc batteryStats; 190 191 public boolean interesting; 192 193 public String baseName; 194 public String name; 195 public int nameWidth; 196 197 // vsize capture when process first detected; can be used to 198 // filter out kernel processes. 199 public long vsize; 200 201 /** 202 * Time in milliseconds. 203 */ 204 public long base_uptime; 205 206 /** 207 * Time in milliseconds. 208 */ 209 public long rel_uptime; 210 211 /** 212 * Time in milliseconds. 213 */ 214 public long base_utime; 215 216 /** 217 * Time in milliseconds. 218 */ 219 public long base_stime; 220 221 /** 222 * Time in milliseconds. 223 */ 224 public int rel_utime; 225 226 /** 227 * Time in milliseconds. 228 */ 229 public int rel_stime; 230 231 public long base_minfaults; 232 public long base_majfaults; 233 public int rel_minfaults; 234 public int rel_majfaults; 235 236 public boolean active; 237 public boolean working; 238 public boolean added; 239 public boolean removed; 240 Stats(int _pid, int parentPid, boolean includeThreads)241 Stats(int _pid, int parentPid, boolean includeThreads) { 242 pid = _pid; 243 if (parentPid < 0) { 244 final File procDir = new File("/proc", Integer.toString(pid)); 245 statFile = new File(procDir, "stat").toString(); 246 cmdlineFile = new File(procDir, "cmdline").toString(); 247 threadsDir = (new File(procDir, "task")).toString(); 248 if (includeThreads) { 249 threadStats = new ArrayList<Stats>(); 250 workingThreads = new ArrayList<Stats>(); 251 } else { 252 threadStats = null; 253 workingThreads = null; 254 } 255 } else { 256 final File procDir = new File("/proc", Integer.toString( 257 parentPid)); 258 final File taskDir = new File( 259 new File(procDir, "task"), Integer.toString(pid)); 260 statFile = new File(taskDir, "stat").toString(); 261 cmdlineFile = null; 262 threadsDir = null; 263 threadStats = null; 264 workingThreads = null; 265 } 266 uid = FileUtils.getUid(statFile.toString()); 267 } 268 } 269 270 private final static Comparator<Stats> sLoadComparator = new Comparator<Stats>() { 271 public final int 272 compare(Stats sta, Stats stb) { 273 int ta = sta.rel_utime + sta.rel_stime; 274 int tb = stb.rel_utime + stb.rel_stime; 275 if (ta != tb) { 276 return ta > tb ? -1 : 1; 277 } 278 if (sta.added != stb.added) { 279 return sta.added ? -1 : 1; 280 } 281 if (sta.removed != stb.removed) { 282 return sta.added ? -1 : 1; 283 } 284 return 0; 285 } 286 }; 287 288 ProcessCpuTracker(boolean includeThreads)289 public ProcessCpuTracker(boolean includeThreads) { 290 mIncludeThreads = includeThreads; 291 long jiffyHz = Libcore.os.sysconf(OsConstants._SC_CLK_TCK); 292 mJiffyMillis = 1000/jiffyHz; 293 } 294 onLoadChanged(float load1, float load5, float load15)295 public void onLoadChanged(float load1, float load5, float load15) { 296 } 297 onMeasureProcessName(String name)298 public int onMeasureProcessName(String name) { 299 return 0; 300 } 301 init()302 public void init() { 303 if (DEBUG) Slog.v(TAG, "Init: " + this); 304 mFirst = true; 305 update(); 306 } 307 update()308 public void update() { 309 if (DEBUG) Slog.v(TAG, "Update: " + this); 310 311 final long nowUptime = SystemClock.uptimeMillis(); 312 final long nowRealtime = SystemClock.elapsedRealtime(); 313 final long nowWallTime = System.currentTimeMillis(); 314 315 final long[] sysCpu = mSystemCpuData; 316 if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, 317 null, sysCpu, null)) { 318 // Total user time is user + nice time. 319 final long usertime = (sysCpu[0]+sysCpu[1]) * mJiffyMillis; 320 // Total system time is simply system time. 321 final long systemtime = sysCpu[2] * mJiffyMillis; 322 // Total idle time is simply idle time. 323 final long idletime = sysCpu[3] * mJiffyMillis; 324 // Total irq time is iowait + irq + softirq time. 325 final long iowaittime = sysCpu[4] * mJiffyMillis; 326 final long irqtime = sysCpu[5] * mJiffyMillis; 327 final long softirqtime = sysCpu[6] * mJiffyMillis; 328 329 // This code is trying to avoid issues with idle time going backwards, 330 // but currently it gets into situations where it triggers most of the time. :( 331 if (true || (usertime >= mBaseUserTime && systemtime >= mBaseSystemTime 332 && iowaittime >= mBaseIoWaitTime && irqtime >= mBaseIrqTime 333 && softirqtime >= mBaseSoftIrqTime && idletime >= mBaseIdleTime)) { 334 mRelUserTime = (int)(usertime - mBaseUserTime); 335 mRelSystemTime = (int)(systemtime - mBaseSystemTime); 336 mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime); 337 mRelIrqTime = (int)(irqtime - mBaseIrqTime); 338 mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime); 339 mRelIdleTime = (int)(idletime - mBaseIdleTime); 340 mRelStatsAreGood = true; 341 342 if (DEBUG) { 343 Slog.i("Load", "Total U:" + (sysCpu[0]*mJiffyMillis) 344 + " N:" + (sysCpu[1]*mJiffyMillis) 345 + " S:" + (sysCpu[2]*mJiffyMillis) + " I:" + (sysCpu[3]*mJiffyMillis) 346 + " W:" + (sysCpu[4]*mJiffyMillis) + " Q:" + (sysCpu[5]*mJiffyMillis) 347 + " O:" + (sysCpu[6]*mJiffyMillis)); 348 Slog.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime 349 + " I:" + mRelIdleTime + " Q:" + mRelIrqTime); 350 } 351 352 mBaseUserTime = usertime; 353 mBaseSystemTime = systemtime; 354 mBaseIoWaitTime = iowaittime; 355 mBaseIrqTime = irqtime; 356 mBaseSoftIrqTime = softirqtime; 357 mBaseIdleTime = idletime; 358 359 } else { 360 mRelUserTime = 0; 361 mRelSystemTime = 0; 362 mRelIoWaitTime = 0; 363 mRelIrqTime = 0; 364 mRelSoftIrqTime = 0; 365 mRelIdleTime = 0; 366 mRelStatsAreGood = false; 367 Slog.w(TAG, "/proc/stats has gone backwards; skipping CPU update"); 368 return; 369 } 370 } 371 372 mLastSampleTime = mCurrentSampleTime; 373 mCurrentSampleTime = nowUptime; 374 mLastSampleRealTime = mCurrentSampleRealTime; 375 mCurrentSampleRealTime = nowRealtime; 376 mLastSampleWallTime = mCurrentSampleWallTime; 377 mCurrentSampleWallTime = nowWallTime; 378 379 final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); 380 try { 381 mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats); 382 } finally { 383 StrictMode.setThreadPolicy(savedPolicy); 384 } 385 386 final float[] loadAverages = mLoadAverageData; 387 if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT, 388 null, null, loadAverages)) { 389 float load1 = loadAverages[0]; 390 float load5 = loadAverages[1]; 391 float load15 = loadAverages[2]; 392 if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) { 393 mLoad1 = load1; 394 mLoad5 = load5; 395 mLoad15 = load15; 396 onLoadChanged(load1, load5, load15); 397 } 398 } 399 400 if (DEBUG) Slog.i(TAG, "*** TIME TO COLLECT STATS: " 401 + (SystemClock.uptimeMillis()-mCurrentSampleTime)); 402 403 mWorkingProcsSorted = false; 404 mFirst = false; 405 } 406 collectStats(String statsFile, int parentPid, boolean first, int[] curPids, ArrayList<Stats> allProcs)407 private int[] collectStats(String statsFile, int parentPid, boolean first, 408 int[] curPids, ArrayList<Stats> allProcs) { 409 410 int[] pids = Process.getPids(statsFile, curPids); 411 int NP = (pids == null) ? 0 : pids.length; 412 int NS = allProcs.size(); 413 int curStatsIndex = 0; 414 for (int i=0; i<NP; i++) { 415 int pid = pids[i]; 416 if (pid < 0) { 417 NP = pid; 418 break; 419 } 420 Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null; 421 422 if (st != null && st.pid == pid) { 423 // Update an existing process... 424 st.added = false; 425 st.working = false; 426 curStatsIndex++; 427 if (DEBUG) Slog.v(TAG, "Existing " 428 + (parentPid < 0 ? "process" : "thread") 429 + " pid " + pid + ": " + st); 430 431 if (st.interesting) { 432 final long uptime = SystemClock.uptimeMillis(); 433 434 final long[] procStats = mProcessStatsData; 435 if (!Process.readProcFile(st.statFile.toString(), 436 PROCESS_STATS_FORMAT, null, procStats, null)) { 437 continue; 438 } 439 440 final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS]; 441 final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS]; 442 final long utime = procStats[PROCESS_STAT_UTIME] * mJiffyMillis; 443 final long stime = procStats[PROCESS_STAT_STIME] * mJiffyMillis; 444 445 if (utime == st.base_utime && stime == st.base_stime) { 446 st.rel_utime = 0; 447 st.rel_stime = 0; 448 st.rel_minfaults = 0; 449 st.rel_majfaults = 0; 450 if (st.active) { 451 st.active = false; 452 } 453 continue; 454 } 455 456 if (!st.active) { 457 st.active = true; 458 } 459 460 if (parentPid < 0) { 461 getName(st, st.cmdlineFile); 462 if (st.threadStats != null) { 463 mCurThreadPids = collectStats(st.threadsDir, pid, false, 464 mCurThreadPids, st.threadStats); 465 } 466 } 467 468 if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid 469 + " utime=" + utime + "-" + st.base_utime 470 + " stime=" + stime + "-" + st.base_stime 471 + " minfaults=" + minfaults + "-" + st.base_minfaults 472 + " majfaults=" + majfaults + "-" + st.base_majfaults); 473 474 st.rel_uptime = uptime - st.base_uptime; 475 st.base_uptime = uptime; 476 st.rel_utime = (int)(utime - st.base_utime); 477 st.rel_stime = (int)(stime - st.base_stime); 478 st.base_utime = utime; 479 st.base_stime = stime; 480 st.rel_minfaults = (int)(minfaults - st.base_minfaults); 481 st.rel_majfaults = (int)(majfaults - st.base_majfaults); 482 st.base_minfaults = minfaults; 483 st.base_majfaults = majfaults; 484 st.working = true; 485 } 486 487 continue; 488 } 489 490 if (st == null || st.pid > pid) { 491 // We have a new process! 492 st = new Stats(pid, parentPid, mIncludeThreads); 493 allProcs.add(curStatsIndex, st); 494 curStatsIndex++; 495 NS++; 496 if (DEBUG) Slog.v(TAG, "New " 497 + (parentPid < 0 ? "process" : "thread") 498 + " pid " + pid + ": " + st); 499 500 final String[] procStatsString = mProcessFullStatsStringData; 501 final long[] procStats = mProcessFullStatsData; 502 st.base_uptime = SystemClock.uptimeMillis(); 503 String path = st.statFile.toString(); 504 //Slog.d(TAG, "Reading proc file: " + path); 505 if (Process.readProcFile(path, PROCESS_FULL_STATS_FORMAT, procStatsString, 506 procStats, null)) { 507 // This is a possible way to filter out processes that 508 // are actually kernel threads... do we want to? Some 509 // of them do use CPU, but there can be a *lot* that are 510 // not doing anything. 511 st.vsize = procStats[PROCESS_FULL_STAT_VSIZE]; 512 if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) { 513 st.interesting = true; 514 st.baseName = procStatsString[0]; 515 st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS]; 516 st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS]; 517 st.base_utime = procStats[PROCESS_FULL_STAT_UTIME] * mJiffyMillis; 518 st.base_stime = procStats[PROCESS_FULL_STAT_STIME] * mJiffyMillis; 519 } else { 520 Slog.i(TAG, "Skipping kernel process pid " + pid 521 + " name " + procStatsString[0]); 522 st.baseName = procStatsString[0]; 523 } 524 } else { 525 Slog.w(TAG, "Skipping unknown process pid " + pid); 526 st.baseName = "<unknown>"; 527 st.base_utime = st.base_stime = 0; 528 st.base_minfaults = st.base_majfaults = 0; 529 } 530 531 if (parentPid < 0) { 532 getName(st, st.cmdlineFile); 533 if (st.threadStats != null) { 534 mCurThreadPids = collectStats(st.threadsDir, pid, true, 535 mCurThreadPids, st.threadStats); 536 } 537 } else if (st.interesting) { 538 st.name = st.baseName; 539 st.nameWidth = onMeasureProcessName(st.name); 540 } 541 542 if (DEBUG) Slog.v("Load", "Stats added " + st.name + " pid=" + st.pid 543 + " utime=" + st.base_utime + " stime=" + st.base_stime 544 + " minfaults=" + st.base_minfaults + " majfaults=" + st.base_majfaults); 545 546 st.rel_utime = 0; 547 st.rel_stime = 0; 548 st.rel_minfaults = 0; 549 st.rel_majfaults = 0; 550 st.added = true; 551 if (!first && st.interesting) { 552 st.working = true; 553 } 554 continue; 555 } 556 557 // This process has gone away! 558 st.rel_utime = 0; 559 st.rel_stime = 0; 560 st.rel_minfaults = 0; 561 st.rel_majfaults = 0; 562 st.removed = true; 563 st.working = true; 564 allProcs.remove(curStatsIndex); 565 NS--; 566 if (DEBUG) Slog.v(TAG, "Removed " 567 + (parentPid < 0 ? "process" : "thread") 568 + " pid " + pid + ": " + st); 569 // Decrement the loop counter so that we process the current pid 570 // again the next time through the loop. 571 i--; 572 continue; 573 } 574 575 while (curStatsIndex < NS) { 576 // This process has gone away! 577 final Stats st = allProcs.get(curStatsIndex); 578 st.rel_utime = 0; 579 st.rel_stime = 0; 580 st.rel_minfaults = 0; 581 st.rel_majfaults = 0; 582 st.removed = true; 583 st.working = true; 584 allProcs.remove(curStatsIndex); 585 NS--; 586 if (localLOGV) Slog.v(TAG, "Removed pid " + st.pid + ": " + st); 587 } 588 589 return pids; 590 } 591 592 /** 593 * Returns the total time (in milliseconds) spent executing in 594 * both user and system code. Safe to call without lock held. 595 */ 596 public long getCpuTimeForPid(int pid) { 597 synchronized (mSinglePidStatsData) { 598 final String statFile = "/proc/" + pid + "/stat"; 599 final long[] statsData = mSinglePidStatsData; 600 if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT, 601 null, statsData, null)) { 602 long time = statsData[PROCESS_STAT_UTIME] 603 + statsData[PROCESS_STAT_STIME]; 604 return time * mJiffyMillis; 605 } 606 return 0; 607 } 608 } 609 610 /** 611 * @return time in milliseconds. 612 */ 613 final public int getLastUserTime() { 614 return mRelUserTime; 615 } 616 617 /** 618 * @return time in milliseconds. 619 */ 620 final public int getLastSystemTime() { 621 return mRelSystemTime; 622 } 623 624 /** 625 * @return time in milliseconds. 626 */ 627 final public int getLastIoWaitTime() { 628 return mRelIoWaitTime; 629 } 630 631 /** 632 * @return time in milliseconds. 633 */ 634 final public int getLastIrqTime() { 635 return mRelIrqTime; 636 } 637 638 /** 639 * @return time in milliseconds. 640 */ 641 final public int getLastSoftIrqTime() { 642 return mRelSoftIrqTime; 643 } 644 645 /** 646 * @return time in milliseconds. 647 */ 648 final public int getLastIdleTime() { 649 return mRelIdleTime; 650 } 651 652 final public boolean hasGoodLastStats() { 653 return mRelStatsAreGood; 654 } 655 656 final public float getTotalCpuPercent() { 657 int denom = mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime; 658 if (denom <= 0) { 659 return 0; 660 } 661 return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100) / denom; 662 } 663 664 final void buildWorkingProcs() { 665 if (!mWorkingProcsSorted) { 666 mWorkingProcs.clear(); 667 final int N = mProcStats.size(); 668 for (int i=0; i<N; i++) { 669 Stats stats = mProcStats.get(i); 670 if (stats.working) { 671 mWorkingProcs.add(stats); 672 if (stats.threadStats != null && stats.threadStats.size() > 1) { 673 stats.workingThreads.clear(); 674 final int M = stats.threadStats.size(); 675 for (int j=0; j<M; j++) { 676 Stats tstats = stats.threadStats.get(j); 677 if (tstats.working) { 678 stats.workingThreads.add(tstats); 679 } 680 } 681 Collections.sort(stats.workingThreads, sLoadComparator); 682 } 683 } 684 } 685 Collections.sort(mWorkingProcs, sLoadComparator); 686 mWorkingProcsSorted = true; 687 } 688 } 689 690 final public int countStats() { 691 return mProcStats.size(); 692 } 693 694 final public Stats getStats(int index) { 695 return mProcStats.get(index); 696 } 697 698 final public int countWorkingStats() { 699 buildWorkingProcs(); 700 return mWorkingProcs.size(); 701 } 702 703 final public Stats getWorkingStats(int index) { 704 return mWorkingProcs.get(index); 705 } 706 707 final public String printCurrentLoad() { 708 StringWriter sw = new StringWriter(); 709 PrintWriter pw = new FastPrintWriter(sw, false, 128); 710 pw.print("Load: "); 711 pw.print(mLoad1); 712 pw.print(" / "); 713 pw.print(mLoad5); 714 pw.print(" / "); 715 pw.println(mLoad15); 716 pw.flush(); 717 return sw.toString(); 718 } 719 720 final public String printCurrentState(long now) { 721 final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); 722 723 buildWorkingProcs(); 724 725 StringWriter sw = new StringWriter(); 726 PrintWriter pw = new FastPrintWriter(sw, false, 1024); 727 728 pw.print("CPU usage from "); 729 if (now > mLastSampleTime) { 730 pw.print(now-mLastSampleTime); 731 pw.print("ms to "); 732 pw.print(now-mCurrentSampleTime); 733 pw.print("ms ago"); 734 } else { 735 pw.print(mLastSampleTime-now); 736 pw.print("ms to "); 737 pw.print(mCurrentSampleTime-now); 738 pw.print("ms later"); 739 } 740 pw.print(" ("); 741 pw.print(sdf.format(new Date(mLastSampleWallTime))); 742 pw.print(" to "); 743 pw.print(sdf.format(new Date(mCurrentSampleWallTime))); 744 pw.print(")"); 745 746 long sampleTime = mCurrentSampleTime - mLastSampleTime; 747 long sampleRealTime = mCurrentSampleRealTime - mLastSampleRealTime; 748 long percAwake = sampleRealTime > 0 ? ((sampleTime*100) / sampleRealTime) : 0; 749 if (percAwake != 100) { 750 pw.print(" with "); 751 pw.print(percAwake); 752 pw.print("% awake"); 753 } 754 pw.println(":"); 755 756 final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime 757 + mRelIrqTime + mRelSoftIrqTime + mRelIdleTime; 758 759 if (DEBUG) Slog.i(TAG, "totalTime " + totalTime + " over sample time " 760 + (mCurrentSampleTime-mLastSampleTime)); 761 762 int N = mWorkingProcs.size(); 763 for (int i=0; i<N; i++) { 764 Stats st = mWorkingProcs.get(i); 765 printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": " "), 766 st.pid, st.name, (int)st.rel_uptime, 767 st.rel_utime, st.rel_stime, 0, 0, 0, st.rel_minfaults, st.rel_majfaults); 768 if (!st.removed && st.workingThreads != null) { 769 int M = st.workingThreads.size(); 770 for (int j=0; j<M; j++) { 771 Stats tst = st.workingThreads.get(j); 772 printProcessCPU(pw, 773 tst.added ? " +" : (tst.removed ? " -": " "), 774 tst.pid, tst.name, (int)st.rel_uptime, 775 tst.rel_utime, tst.rel_stime, 0, 0, 0, 0, 0); 776 } 777 } 778 } 779 780 printProcessCPU(pw, "", -1, "TOTAL", totalTime, mRelUserTime, mRelSystemTime, 781 mRelIoWaitTime, mRelIrqTime, mRelSoftIrqTime, 0, 0); 782 783 pw.flush(); 784 return sw.toString(); 785 } 786 787 private void printRatio(PrintWriter pw, long numerator, long denominator) { 788 long thousands = (numerator*1000)/denominator; 789 long hundreds = thousands/10; 790 pw.print(hundreds); 791 if (hundreds < 10) { 792 long remainder = thousands - (hundreds*10); 793 if (remainder != 0) { 794 pw.print('.'); 795 pw.print(remainder); 796 } 797 } 798 } 799 800 private void printProcessCPU(PrintWriter pw, String prefix, int pid, String label, 801 int totalTime, int user, int system, int iowait, int irq, int softIrq, 802 int minFaults, int majFaults) { 803 pw.print(prefix); 804 if (totalTime == 0) totalTime = 1; 805 printRatio(pw, user+system+iowait+irq+softIrq, totalTime); 806 pw.print("% "); 807 if (pid >= 0) { 808 pw.print(pid); 809 pw.print("/"); 810 } 811 pw.print(label); 812 pw.print(": "); 813 printRatio(pw, user, totalTime); 814 pw.print("% user + "); 815 printRatio(pw, system, totalTime); 816 pw.print("% kernel"); 817 if (iowait > 0) { 818 pw.print(" + "); 819 printRatio(pw, iowait, totalTime); 820 pw.print("% iowait"); 821 } 822 if (irq > 0) { 823 pw.print(" + "); 824 printRatio(pw, irq, totalTime); 825 pw.print("% irq"); 826 } 827 if (softIrq > 0) { 828 pw.print(" + "); 829 printRatio(pw, softIrq, totalTime); 830 pw.print("% softirq"); 831 } 832 if (minFaults > 0 || majFaults > 0) { 833 pw.print(" / faults:"); 834 if (minFaults > 0) { 835 pw.print(" "); 836 pw.print(minFaults); 837 pw.print(" minor"); 838 } 839 if (majFaults > 0) { 840 pw.print(" "); 841 pw.print(majFaults); 842 pw.print(" major"); 843 } 844 } 845 pw.println(); 846 } 847 848 private String readFile(String file, char endChar) { 849 // Permit disk reads here, as /proc/meminfo isn't really "on 850 // disk" and should be fast. TODO: make BlockGuard ignore 851 // /proc/ and /sys/ files perhaps? 852 StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); 853 FileInputStream is = null; 854 try { 855 is = new FileInputStream(file); 856 int len = is.read(mBuffer); 857 is.close(); 858 859 if (len > 0) { 860 int i; 861 for (i=0; i<len; i++) { 862 if (mBuffer[i] == endChar) { 863 break; 864 } 865 } 866 return new String(mBuffer, 0, i); 867 } 868 } catch (java.io.FileNotFoundException e) { 869 } catch (java.io.IOException e) { 870 } finally { 871 IoUtils.closeQuietly(is); 872 StrictMode.setThreadPolicy(savedPolicy); 873 } 874 return null; 875 } 876 getName(Stats st, String cmdlineFile)877 private void getName(Stats st, String cmdlineFile) { 878 String newName = st.name; 879 if (st.name == null || st.name.equals("app_process") 880 || st.name.equals("<pre-initialized>")) { 881 String cmdName = readFile(cmdlineFile, '\0'); 882 if (cmdName != null && cmdName.length() > 1) { 883 newName = cmdName; 884 int i = newName.lastIndexOf("/"); 885 if (i > 0 && i < newName.length()-1) { 886 newName = newName.substring(i+1); 887 } 888 } 889 if (newName == null) { 890 newName = st.baseName; 891 } 892 } 893 if (st.name == null || !newName.equals(st.name)) { 894 st.name = newName; 895 st.nameWidth = onMeasureProcessName(st.name); 896 } 897 } 898 } 899