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