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