blob: 58f8980c004d193e16ab2d5896a478b66c374004 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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.server;
18
19import static android.os.Process.*;
20
21import android.os.Process;
22import android.os.SystemClock;
23import android.util.Config;
24import android.util.Log;
25
26import java.io.File;
27import java.io.FileInputStream;
28import java.io.PrintWriter;
29import java.io.StringWriter;
30import java.util.ArrayList;
31import java.util.Collections;
32import java.util.Comparator;
33
34public class ProcessStats {
35 private static final String TAG = "ProcessStats";
36 private static final boolean DEBUG = false;
37 private static final boolean localLOGV = DEBUG || Config.LOGV;
38
39 private static final int[] PROCESS_STATS_FORMAT = new int[] {
40 PROC_SPACE_TERM,
41 PROC_SPACE_TERM,
42 PROC_SPACE_TERM,
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,
51 PROC_SPACE_TERM,
52 PROC_SPACE_TERM,
53 PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime
54 PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime
55 };
56
Amith Yamasanieaeb6632009-06-03 15:16:10 -070057 /** Stores user time and system time in 100ths of a second. */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 private final long[] mProcessStatsData = new long[2];
Amith Yamasanieaeb6632009-06-03 15:16:10 -070059 /** Stores user time and system time in 100ths of a second. */
60 private final long[] mSinglePidStatsData = new long[2];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
62 private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
63 PROC_SPACE_TERM,
64 PROC_SPACE_TERM|PROC_PARENS|PROC_OUT_STRING, // 1: name
65 PROC_SPACE_TERM,
66 PROC_SPACE_TERM,
67 PROC_SPACE_TERM,
68 PROC_SPACE_TERM,
69 PROC_SPACE_TERM,
70 PROC_SPACE_TERM,
71 PROC_SPACE_TERM,
72 PROC_SPACE_TERM,
73 PROC_SPACE_TERM,
74 PROC_SPACE_TERM,
75 PROC_SPACE_TERM,
76 PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime
77 PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime
78 };
79
80 private final String[] mProcessFullStatsStringData = new String[3];
81 private final long[] mProcessFullStatsData = new long[3];
82
83 private static final int[] SYSTEM_CPU_FORMAT = new int[] {
84 PROC_SPACE_TERM|PROC_COMBINE,
85 PROC_SPACE_TERM|PROC_OUT_LONG, // 1: user time
86 PROC_SPACE_TERM|PROC_OUT_LONG, // 2: nice time
87 PROC_SPACE_TERM|PROC_OUT_LONG, // 3: sys time
88 PROC_SPACE_TERM|PROC_OUT_LONG, // 4: idle time
89 PROC_SPACE_TERM|PROC_OUT_LONG, // 5: iowait time
90 PROC_SPACE_TERM|PROC_OUT_LONG, // 6: irq time
91 PROC_SPACE_TERM|PROC_OUT_LONG // 7: softirq time
92 };
93
94 private final long[] mSystemCpuData = new long[7];
95
96 private static final int[] LOAD_AVERAGE_FORMAT = new int[] {
97 PROC_SPACE_TERM|PROC_OUT_FLOAT, // 0: 1 min
98 PROC_SPACE_TERM|PROC_OUT_FLOAT, // 1: 5 mins
99 PROC_SPACE_TERM|PROC_OUT_FLOAT // 2: 15 mins
100 };
101
102 private final float[] mLoadAverageData = new float[3];
103
104 private final boolean mIncludeThreads;
105
106 private float mLoad1 = 0;
107 private float mLoad5 = 0;
108 private float mLoad15 = 0;
109
110 private long mCurrentSampleTime;
111 private long mLastSampleTime;
112
113 private long mBaseUserTime;
114 private long mBaseSystemTime;
115 private long mBaseIoWaitTime;
116 private long mBaseIrqTime;
117 private long mBaseSoftIrqTime;
118 private long mBaseIdleTime;
119 private int mRelUserTime;
120 private int mRelSystemTime;
121 private int mRelIoWaitTime;
122 private int mRelIrqTime;
123 private int mRelSoftIrqTime;
124 private int mRelIdleTime;
125
126 private int[] mCurPids;
127 private int[] mCurThreadPids;
128
129 private final ArrayList<Stats> mProcStats = new ArrayList<Stats>();
130 private final ArrayList<Stats> mWorkingProcs = new ArrayList<Stats>();
131 private boolean mWorkingProcsSorted;
132
133 private boolean mFirst = true;
134
135 private byte[] mBuffer = new byte[256];
136
137 public static class Stats {
138 public final int pid;
139 final String statFile;
140 final String cmdlineFile;
141 final String threadsDir;
142 final ArrayList<Stats> threadStats;
143 final ArrayList<Stats> workingThreads;
144
145 public String baseName;
146 public String name;
147 int nameWidth;
148
149 public long base_utime;
150 public long base_stime;
151 public int rel_utime;
152 public int rel_stime;
153
154 public boolean active;
155 public boolean added;
156 public boolean removed;
157
158 Stats(int _pid, int parentPid, boolean includeThreads) {
159 pid = _pid;
160 if (parentPid < 0) {
161 final File procDir = new File("/proc", Integer.toString(pid));
162 statFile = new File(procDir, "stat").toString();
163 cmdlineFile = new File(procDir, "cmdline").toString();
164 threadsDir = (new File(procDir, "task")).toString();
165 if (includeThreads) {
166 threadStats = new ArrayList<Stats>();
167 workingThreads = new ArrayList<Stats>();
168 } else {
169 threadStats = null;
170 workingThreads = null;
171 }
172 } else {
173 final File procDir = new File("/proc", Integer.toString(
174 parentPid));
175 final File taskDir = new File(
176 new File(procDir, "task"), Integer.toString(pid));
177 statFile = new File(taskDir, "stat").toString();
178 cmdlineFile = null;
179 threadsDir = null;
180 threadStats = null;
181 workingThreads = null;
182 }
183 }
184 }
185
186 private final static Comparator<Stats> sLoadComparator = new Comparator<Stats>() {
187 public final int
188 compare(Stats sta, Stats stb)
189 {
190 int ta = sta.rel_utime + sta.rel_stime;
191 int tb = stb.rel_utime + stb.rel_stime;
192 if (ta != tb) {
193 return ta > tb ? -1 : 1;
194 }
195 if (sta.added != stb.added) {
196 return sta.added ? -1 : 1;
197 }
198 if (sta.removed != stb.removed) {
199 return sta.added ? -1 : 1;
200 }
201 return 0;
202 }
203 };
204
205
206 public ProcessStats(boolean includeThreads) {
207 mIncludeThreads = includeThreads;
208 }
209
210 public void onLoadChanged(float load1, float load5, float load15) {
211 }
212
213 public int onMeasureProcessName(String name) {
214 return 0;
215 }
216
217 public void init() {
218 mFirst = true;
219 update();
220 }
221
222 public void update() {
223 mLastSampleTime = mCurrentSampleTime;
224 mCurrentSampleTime = SystemClock.uptimeMillis();
225
226 final float[] loadAverages = mLoadAverageData;
227 if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT,
228 null, null, loadAverages)) {
229 float load1 = loadAverages[0];
230 float load5 = loadAverages[1];
231 float load15 = loadAverages[2];
232 if (load1 != mLoad1 || load5 != mLoad5 || load15 != mLoad15) {
233 mLoad1 = load1;
234 mLoad5 = load5;
235 mLoad15 = load15;
236 onLoadChanged(load1, load5, load15);
237 }
238 }
239
240 mCurPids = collectStats("/proc", -1, mFirst, mCurPids,
241 mProcStats, mWorkingProcs);
242 mFirst = false;
243
244 final long[] sysCpu = mSystemCpuData;
245 if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT,
246 null, sysCpu, null)) {
247 // Total user time is user + nice time.
248 final long usertime = sysCpu[0]+sysCpu[1];
249 // Total system time is simply system time.
250 final long systemtime = sysCpu[2];
251 // Total idle time is simply idle time.
252 final long idletime = sysCpu[3];
253 // Total irq time is iowait + irq + softirq time.
254 final long iowaittime = sysCpu[4];
255 final long irqtime = sysCpu[5];
256 final long softirqtime = sysCpu[6];
257
258 mRelUserTime = (int)(usertime - mBaseUserTime);
259 mRelSystemTime = (int)(systemtime - mBaseSystemTime);
260 mRelIoWaitTime = (int)(iowaittime - mBaseIoWaitTime);
261 mRelIrqTime = (int)(irqtime - mBaseIrqTime);
262 mRelSoftIrqTime = (int)(softirqtime - mBaseSoftIrqTime);
263 mRelIdleTime = (int)(idletime - mBaseIdleTime);
264
265 if (false) {
266 Log.i("Load", "Total U:" + sysCpu[0] + " N:" + sysCpu[1]
267 + " S:" + sysCpu[2] + " I:" + sysCpu[3]
268 + " W:" + sysCpu[4] + " Q:" + sysCpu[5]
269 + " O:" + sysCpu[6]);
270 Log.i("Load", "Rel U:" + mRelUserTime + " S:" + mRelSystemTime
271 + " I:" + mRelIdleTime + " Q:" + mRelIrqTime);
272 }
273
274 mBaseUserTime = usertime;
275 mBaseSystemTime = systemtime;
276 mBaseIoWaitTime = iowaittime;
277 mBaseIrqTime = irqtime;
278 mBaseSoftIrqTime = softirqtime;
279 mBaseIdleTime = idletime;
280 }
281
282 mWorkingProcsSorted = false;
283 mFirst = false;
284 }
285
286 private int[] collectStats(String statsFile, int parentPid, boolean first,
287 int[] curPids, ArrayList<Stats> allProcs,
288 ArrayList<Stats> workingProcs) {
289
290 workingProcs.clear();
291
292 int[] pids = Process.getPids(statsFile, curPids);
293 int NP = (pids == null) ? 0 : pids.length;
294 int NS = allProcs.size();
295 int curStatsIndex = 0;
296 for (int i=0; i<NP; i++) {
297 int pid = pids[i];
298 if (pid < 0) {
299 NP = pid;
300 break;
301 }
302 Stats st = curStatsIndex < NS ? allProcs.get(curStatsIndex) : null;
303
304 if (st != null && st.pid == pid) {
305 // Update an existing process...
306 st.added = false;
307 curStatsIndex++;
308 if (localLOGV) Log.v(TAG, "Existing pid " + pid + ": " + st);
309
310 final long[] procStats = mProcessStatsData;
311 if (!Process.readProcFile(st.statFile.toString(),
312 PROCESS_STATS_FORMAT, null, procStats, null)) {
313 continue;
314 }
315
316 final long utime = procStats[0];
317 final long stime = procStats[1];
318
319 if (utime == st.base_utime && stime == st.base_stime) {
320 st.rel_utime = 0;
321 st.rel_stime = 0;
322 if (st.active) {
323 st.active = false;
324 }
325 continue;
326 }
327
328 if (!st.active) {
329 st.active = true;
330 }
331
332 if (parentPid < 0) {
333 getName(st, st.cmdlineFile);
334 if (st.threadStats != null) {
335 mCurThreadPids = collectStats(st.threadsDir, pid, false,
336 mCurThreadPids, st.threadStats,
337 st.workingThreads);
338 }
339 }
340
341 st.rel_utime = (int)(utime - st.base_utime);
342 st.rel_stime = (int)(stime - st.base_stime);
343 st.base_utime = utime;
344 st.base_stime = stime;
345 //Log.i("Load", "Stats changed " + name + " pid=" + st.pid
346 // + " name=" + st.name + " utime=" + utime
347 // + " stime=" + stime);
348 workingProcs.add(st);
349 continue;
350 }
351
352 if (st == null || st.pid > pid) {
353 // We have a new process!
354 st = new Stats(pid, parentPid, mIncludeThreads);
355 allProcs.add(curStatsIndex, st);
356 curStatsIndex++;
357 NS++;
358 if (localLOGV) Log.v(TAG, "New pid " + pid + ": " + st);
359
360 final String[] procStatsString = mProcessFullStatsStringData;
361 final long[] procStats = mProcessFullStatsData;
362 if (Process.readProcFile(st.statFile.toString(),
363 PROCESS_FULL_STATS_FORMAT, procStatsString,
364 procStats, null)) {
365 st.baseName = parentPid < 0
366 ? procStatsString[0] : Integer.toString(pid);
367 st.base_utime = procStats[1];
368 st.base_stime = procStats[2];
369 } else {
370 st.baseName = "<unknown>";
371 st.base_utime = st.base_stime = 0;
372 }
373
374 if (parentPid < 0) {
375 getName(st, st.cmdlineFile);
376 } else {
377 st.name = st.baseName;
378 st.nameWidth = onMeasureProcessName(st.name);
379 if (st.threadStats != null) {
380 mCurThreadPids = collectStats(st.threadsDir, pid, true,
381 mCurThreadPids, st.threadStats,
382 st.workingThreads);
383 }
384 }
385
386 //Log.i("Load", "New process: " + st.pid + " " + st.name);
387 st.rel_utime = 0;
388 st.rel_stime = 0;
389 st.added = true;
390 if (!first) {
391 workingProcs.add(st);
392 }
393 continue;
394 }
395
396 // This process has gone away!
397 st.rel_utime = 0;
398 st.rel_stime = 0;
399 st.removed = true;
400 workingProcs.add(st);
401 allProcs.remove(curStatsIndex);
402 NS--;
403 if (localLOGV) Log.v(TAG, "Removed pid " + st.pid + ": " + st);
404 // Decrement the loop counter so that we process the current pid
405 // again the next time through the loop.
406 i--;
407 continue;
408 }
409
410 while (curStatsIndex < NS) {
411 // This process has gone away!
412 final Stats st = allProcs.get(curStatsIndex);
413 st.rel_utime = 0;
414 st.rel_stime = 0;
415 st.removed = true;
416 workingProcs.add(st);
417 allProcs.remove(curStatsIndex);
418 NS--;
419 if (localLOGV) Log.v(TAG, "Removed pid " + st.pid + ": " + st);
420 }
421
422 return pids;
423 }
Amith Yamasanieaeb6632009-06-03 15:16:10 -0700424
425 public long getCpuTimeForPid(int pid) {
426 final String statFile = "/proc/" + pid + "/stat";
427 final long[] statsData = mSinglePidStatsData;
428 if (Process.readProcFile(statFile, PROCESS_STATS_FORMAT,
429 null, statsData, null)) {
430 long time = statsData[0] + statsData[1];
431 return time;
432 }
433 return 0;
434 }
435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 final public int getLastUserTime() {
437 return mRelUserTime;
438 }
439
440 final public int getLastSystemTime() {
441 return mRelSystemTime;
442 }
443
444 final public int getLastIoWaitTime() {
445 return mRelIoWaitTime;
446 }
447
448 final public int getLastIrqTime() {
449 return mRelIrqTime;
450 }
451
452 final public int getLastSoftIrqTime() {
453 return mRelSoftIrqTime;
454 }
455
456 final public int getLastIdleTime() {
457 return mRelIdleTime;
458 }
459
460 final public float getTotalCpuPercent() {
461 return ((float)(mRelUserTime+mRelSystemTime+mRelIrqTime)*100)
462 / (mRelUserTime+mRelSystemTime+mRelIrqTime+mRelIdleTime);
463 }
464
465 final public int countWorkingStats() {
466 if (!mWorkingProcsSorted) {
467 Collections.sort(mWorkingProcs, sLoadComparator);
468 mWorkingProcsSorted = true;
469 }
470 return mWorkingProcs.size();
471 }
472
473 final public Stats getWorkingStats(int index) {
474 return mWorkingProcs.get(index);
475 }
476
477 final public String printCurrentState() {
478 if (!mWorkingProcsSorted) {
479 Collections.sort(mWorkingProcs, sLoadComparator);
480 mWorkingProcsSorted = true;
481 }
482
483 StringWriter sw = new StringWriter();
484 PrintWriter pw = new PrintWriter(sw);
485 pw.print("Load: ");
486 pw.print(mLoad1);
487 pw.print(" / ");
488 pw.print(mLoad5);
489 pw.print(" / ");
490 pw.println(mLoad15);
491
492 long now = SystemClock.uptimeMillis();
493
494 pw.print("CPU usage from ");
495 pw.print(now-mLastSampleTime);
496 pw.print("ms to ");
497 pw.print(now-mCurrentSampleTime);
498 pw.println("ms ago:");
499
500 final int totalTime = mRelUserTime + mRelSystemTime + mRelIoWaitTime + mRelIrqTime +
501 mRelSoftIrqTime + mRelIdleTime;
502
503 int N = mWorkingProcs.size();
504 for (int i=0; i<N; i++) {
505 Stats st = mWorkingProcs.get(i);
506 printProcessCPU(pw, st.added ? " +" : (st.removed ? " -": " "),
507 st.name, totalTime, st.rel_utime, st.rel_stime, 0, 0, 0);
508 if (!st.removed && st.workingThreads != null) {
509 int M = st.workingThreads.size();
510 for (int j=0; j<M; j++) {
511 Stats tst = st.workingThreads.get(j);
512 printProcessCPU(pw,
513 tst.added ? " +" : (tst.removed ? " -": " "),
514 tst.name, totalTime, tst.rel_utime, tst.rel_stime, 0, 0, 0);
515 }
516 }
517 }
518
519 printProcessCPU(pw, "", "TOTAL", totalTime, mRelUserTime, mRelSystemTime, mRelIoWaitTime,
520 mRelIrqTime, mRelSoftIrqTime);
521
522 return sw.toString();
523 }
524
525 private void printProcessCPU(PrintWriter pw, String prefix, String label, int totalTime,
526 int user, int system, int iowait, int irq, int softIrq) {
527 pw.print(prefix);
528 pw.print(label);
529 pw.print(": ");
530 if (totalTime == 0) totalTime = 1;
531 pw.print(((user+system+iowait+irq+softIrq)*100)/totalTime);
532 pw.print("% = ");
533 pw.print((user*100)/totalTime);
534 pw.print("% user + ");
535 pw.print((system*100)/totalTime);
536 pw.print("% kernel");
537 if (iowait > 0) {
538 pw.print(" + ");
539 pw.print((iowait*100)/totalTime);
540 pw.print("% iowait");
541 }
542 if (irq > 0) {
543 pw.print(" + ");
544 pw.print((irq*100)/totalTime);
545 pw.print("% irq");
546 }
547 if (softIrq > 0) {
548 pw.print(" + ");
549 pw.print((softIrq*100)/totalTime);
550 pw.print("% softirq");
551 }
552 pw.println();
553 }
554
555 private String readFile(String file, char endChar) {
556 try {
557 FileInputStream is = new FileInputStream(file);
558 int len = is.read(mBuffer);
559 is.close();
560
561 if (len > 0) {
562 int i;
563 for (i=0; i<len; i++) {
564 if (mBuffer[i] == endChar) {
565 break;
566 }
567 }
568 return new String(mBuffer, 0, 0, i);
569 }
570 } catch (java.io.FileNotFoundException e) {
571 } catch (java.io.IOException e) {
572 }
573 return null;
574 }
575
576 private void getName(Stats st, String cmdlineFile) {
577 String newName = st.baseName;
578 if (st.baseName == null || st.baseName.equals("app_process")) {
579 String cmdName = readFile(cmdlineFile, '\0');
580 if (cmdName != null && cmdName.length() > 1) {
581 newName = cmdName;
582 int i = newName.lastIndexOf("/");
583 if (i > 0 && i < newName.length()-1) {
584 newName = newName.substring(i+1);
585 }
586 }
587 }
588 if (st.name == null || !newName.equals(st.name)) {
589 st.name = newName;
590 st.nameWidth = onMeasureProcessName(st.name);
591 }
592 }
593}
594