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