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