blob: d550d4d9b7cf7013e8e508700e9ba0bdf167c1be [file] [log] [blame]
Arjan van der Ven28615352007-05-11 20:08:05 +00001/*
Arjan van der Venee42d812007-05-18 18:33:19 +00002 * Copyright 2007, Intel Corporation
Arjan van der Ven0eb44da2007-05-13 03:39:32 +00003 *
Arjan van der Ven28615352007-05-11 20:08:05 +00004 * This file is part of PowerTOP
5 *
6 * This program file is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; version 2 of the License.
Arjan van der Ven0eb44da2007-05-13 03:39:32 +00009 *
Arjan van der Ven28615352007-05-11 20:08:05 +000010 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
Arjan van der Ven0eb44da2007-05-13 03:39:32 +000014 *
Arjan van der Ven28615352007-05-11 20:08:05 +000015 * You should have received a copy of the GNU General Public License
Arjan van der Ven0eb44da2007-05-13 03:39:32 +000016 * along with this program in a file named COPYING; if not, write to the
17 * Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor,
Arjan van der Ven28615352007-05-11 20:08:05 +000019 * Boston, MA 02110-1301 USA
20 *
21 * Authors:
22 * Arjan van de Ven <arjan@linux.intel.com>
23 */
24
Arjan van der Ven6fe1c572007-08-18 21:52:07 +000025#include <getopt.h>
Arjan van der Ven28615352007-05-11 20:08:05 +000026#include <unistd.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <stdint.h>
31#include <sys/types.h>
32#include <dirent.h>
Arjan van der Ven41cc9942007-05-24 20:20:19 +000033#include <ctype.h>
Arjan van der Ven32724fc2007-05-24 21:06:24 +000034#include <assert.h>
Arjan van der Ven6a0ade22007-05-24 23:45:33 +000035#include <locale.h>
Arjan van der Ven2e74d1d2007-05-26 15:47:40 +000036#include <time.h>
Arjan van der Ven70551c52008-03-27 03:16:37 +000037#include <sys/stat.h>
Arjan van der Ven28615352007-05-11 20:08:05 +000038
Arjan van der Ven9ed46282007-05-13 17:17:29 +000039#include "powertop.h"
40
Anurag Singhc5781f82011-10-10 10:44:44 -070041#define VERSION "1.11-1"
Auke Kokeecdec52008-12-01 18:22:47 +000042
Arjan van der Ven28615352007-05-11 20:08:05 +000043uint64_t start_usage[8], start_duration[8];
44uint64_t last_usage[8], last_duration[8];
Arjan van der Ven03f2a892008-06-27 17:01:33 +000045char cnames[8][16];
Arjan van der Ven28615352007-05-11 20:08:05 +000046
Arjan van der Ven6fe1c572007-08-18 21:52:07 +000047double ticktime = 15.0;
Arjan van der Ven28615352007-05-11 20:08:05 +000048
Arjan van der Ven7144a6b2007-05-26 00:00:53 +000049int interrupt_0, total_interrupt;
Arjan van der Ven28615352007-05-11 20:08:05 +000050
Arjan van der Ven356cd2c2007-12-13 21:52:01 +000051int showpids = 0;
52
Arjan van der Vend252c312007-05-12 18:22:26 +000053static int maxcstate = 0;
Arjan van der Ven5288ca72007-05-23 16:24:06 +000054int topcstate = 0;
Arjan van der Vend252c312007-05-12 18:22:26 +000055
Arjan van der Ven6fe1c572007-08-18 21:52:07 +000056int dump = 0;
Anurag Singhc5781f82011-10-10 10:44:44 -070057int reset_pm_stats = 0;
Arjan van der Ven6fe1c572007-08-18 21:52:07 +000058
Patrick Caind5f3cfb2012-10-24 12:33:15 -070059static int cpu_count = 0;
60
Arjan van der Ven5f4b71e2007-10-27 21:46:59 +000061#define IRQCOUNT 150
Arjan van der Ven28615352007-05-11 20:08:05 +000062
63struct irqdata {
64 int active;
65 int number;
66 uint64_t count;
67 char description[256];
68};
69
70struct irqdata interrupts[IRQCOUNT];
71
Arjan van der Ven70551c52008-03-27 03:16:37 +000072#define FREQ_ACPI 3579.545
73static unsigned long FREQ;
Arjan van der Ven28615352007-05-11 20:08:05 +000074
75int nostats;
76
Arjan van der Ven6473da42007-05-13 15:23:31 +000077
78struct line *lines;
79int linehead;
80int linesize;
81int linectotal;
Arjan van der Ven28615352007-05-11 20:08:05 +000082
Arjan van der Ven2e74d1d2007-05-26 15:47:40 +000083double last_bat_cap = 0;
84double prev_bat_cap = 0;
85time_t last_bat_time = 0;
86time_t prev_bat_time = 0;
87
Arjan van der Ven2b7db8e2007-06-23 19:03:34 +000088double displaytime = 0.0;
89
Arjan van der Ven28615352007-05-11 20:08:05 +000090void push_line(char *string, int count)
91{
92 int i;
Arjan van der Ven32724fc2007-05-24 21:06:24 +000093
94 assert(string != NULL);
Arjan van der Ven12e55ca2007-05-12 22:52:54 +000095 for (i = 0; i < linehead; i++)
Arjan van der Ven6473da42007-05-13 15:23:31 +000096 if (strcmp(string, lines[i].string) == 0) {
97 lines[i].count += count;
Arjan van der Ven28615352007-05-11 20:08:05 +000098 return;
99 }
Arjan van der Ven6473da42007-05-13 15:23:31 +0000100 if (linehead == linesize)
101 lines = realloc (lines, (linesize ? (linesize *= 2) : (linesize = 64)) * sizeof (struct line));
102 lines[linehead].string = strdup (string);
103 lines[linehead].count = count;
Arjan van der Ven356cd2c2007-12-13 21:52:01 +0000104 lines[linehead].pid[0] = 0;
105 linehead++;
106}
107
108void push_line_pid(char *string, int count, char *pid)
109{
110 int i;
111 assert(string != NULL);
112 for (i = 0; i < linehead; i++)
113 if (strcmp(string, lines[i].string) == 0) {
114 lines[i].count += count;
115 if (pid && strcmp(lines[i].pid, pid)!=0)
116 lines[i].pid[0] = 0;
117 return;
118 }
119 if (linehead == linesize)
120 lines = realloc (lines, (linesize ? (linesize *= 2) : (linesize = 64)) * sizeof (struct line));
121 lines[linehead].string = strdup (string);
122 lines[linehead].count = count;
123 if (pid)
124 strcpy(lines[linehead].pid, pid);
Arjan van der Ven28615352007-05-11 20:08:05 +0000125 linehead++;
126}
127
128void clear_lines(void)
129{
Arjan van der Ven6473da42007-05-13 15:23:31 +0000130 int i;
131 for (i = 0; i < linehead; i++)
132 free (lines[i].string);
133 free (lines);
134 linehead = linesize = 0;
135 lines = NULL;
Arjan van der Ven28615352007-05-11 20:08:05 +0000136}
137
138void count_lines(void)
139{
140 uint64_t q = 0;
141 int i;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000142 for (i = 0; i < linehead; i++)
Anurag Singhbf459e22011-10-10 10:50:51 -0700143 q += lines[i].count;
144 linectotal = q;
Arjan van der Ven28615352007-05-11 20:08:05 +0000145}
146
147int update_irq(int irq, uint64_t count, char *name)
148{
149 int i;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000150 int firstfree = IRQCOUNT;
Arjan van der Vena18feb22007-05-24 21:02:02 +0000151
152 if (!name)
153 return 0;
154
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000155 for (i = 0; i < IRQCOUNT; i++) {
Arjan van der Ven28615352007-05-11 20:08:05 +0000156 if (interrupts[i].active && interrupts[i].number == irq) {
157 uint64_t oldcount;
158 oldcount = interrupts[i].count;
159 interrupts[i].count = count;
160 return count - oldcount;
161 }
162 if (!interrupts[i].active && firstfree > i)
163 firstfree = i;
164 }
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000165
Arjan van der Ven28615352007-05-11 20:08:05 +0000166 interrupts[firstfree].active = 1;
167 interrupts[firstfree].count = count;
168 interrupts[firstfree].number = irq;
169 strcpy(interrupts[firstfree].description, name);
Arjan van der Venf0963632007-08-18 21:28:02 +0000170 if (strcmp(name,"i8042\n")==0)
171 strcpy(interrupts[firstfree].description, _("PS/2 keyboard/mouse/touchpad"));
Arjan van der Ven28615352007-05-11 20:08:05 +0000172 return count;
173}
174
175static void do_proc_irq(void)
176{
177 FILE *file;
178 char line[1024];
Arjan van der Ven25e737e2007-05-27 15:20:18 +0000179 char line2[1024];
Arjan van der Ven28615352007-05-11 20:08:05 +0000180 char *name;
181 uint64_t delta;
Arjan van der Ven4be0e1b2007-05-23 15:50:53 +0000182
183 interrupt_0 = 0;
Arjan van der Ven7144a6b2007-05-26 00:00:53 +0000184 total_interrupt = 0;
Arjan van der Ven4be0e1b2007-05-23 15:50:53 +0000185
Arjan van der Ven28615352007-05-11 20:08:05 +0000186 file = fopen("/proc/interrupts", "r");
187 if (!file)
188 return;
189 while (!feof(file)) {
190 char *c;
191 int nr = -1;
192 uint64_t count = 0;
Arjan van der Ven5f4b71e2007-10-27 21:46:59 +0000193 int special = 0;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000194 memset(line, 0, sizeof(line));
Arjan van der Venef08f1c2007-05-16 18:28:57 +0000195 if (fgets(line, 1024, file) == NULL)
196 break;
Arjan van der Ven28615352007-05-11 20:08:05 +0000197 c = strchr(line, ':');
198 if (!c)
199 continue;
Arjan van der Ven5f4b71e2007-10-27 21:46:59 +0000200 /* deal with NMI and the like.. make up fake nrs */
201 if (line[0] != ' ' && (line[0] < '0' || line[0] > '9')) {
202 if (strncmp(line,"NMI:", 4)==0)
203 nr=20000;
204 if (strncmp(line,"RES:", 4)==0)
205 nr=20001;
206 if (strncmp(line,"CAL:", 4)==0)
207 nr=20002;
208 if (strncmp(line,"TLB:", 4)==0)
209 nr=20003;
210 if (strncmp(line,"TRM:", 4)==0)
211 nr=20004;
212 if (strncmp(line,"THR:", 4)==0)
213 nr=20005;
214 if (strncmp(line,"SPU:", 4)==0)
215 nr=20006;
216 special = 1;
217 } else
218 nr = strtoull(line, NULL, 10);
219
220 if (nr==-1)
Arjan van der Ven28615352007-05-11 20:08:05 +0000221 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000222 *c = 0;
Arjan van der Ven28615352007-05-11 20:08:05 +0000223 c++;
224 while (c && strlen(c)) {
225 char *newc;
226 count += strtoull(c, &newc, 10);
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000227 if (newc == c)
Arjan van der Ven28615352007-05-11 20:08:05 +0000228 break;
229 c = newc;
230 }
231 c = strchr(c, ' ');
Anurag Singhbf459e22011-10-10 10:50:51 -0700232 if (!c)
233 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000234 while (c && *c == ' ')
235 c++;
Arjan van der Ven5f4b71e2007-10-27 21:46:59 +0000236 if (!special) {
237 c = strchr(c, ' ');
238 if (!c)
239 continue;
240 while (c && *c == ' ')
241 c++;
242 }
Arjan van der Ven28615352007-05-11 20:08:05 +0000243 name = c;
Arjan van der Ven28615352007-05-11 20:08:05 +0000244 c = strchr(name, '\n');
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000245 if (c)
246 *c = 0;
Anurag Singhbf459e22011-10-10 10:50:51 -0700247
248 delta = update_irq(nr, count, name);
Arjan van der Ven7fe991f2007-10-27 22:49:26 +0000249 if (strcmp(name, "i8042")) {
Anurag Singhbf459e22011-10-10 10:50:51 -0700250 if (special)
251 sprintf(line2, _(" <kernel IPI> : %s"), name);
252 else
253 sprintf(line2, _(" <interrupt> : %s"), name);
Arjan van der Ven7fe991f2007-10-27 22:49:26 +0000254 }
Arjan van der Venf0963632007-08-18 21:28:02 +0000255 else
Anurag Singhbf459e22011-10-10 10:50:51 -0700256 sprintf(line2, _(" <interrupt> : %s"), _("PS/2 keyboard/mouse/touchpad"));
257 //don't special-case interrupt 0 as it's not the system timer on Android
258#ifdef PLATFORM_NO_INT0
259 if (delta > 0)
260 push_line(line2, delta);
261 total_interrupt += delta;
262#else
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000263 if (nr > 0 && delta > 0)
Arjan van der Ven25e737e2007-05-27 15:20:18 +0000264 push_line(line2, delta);
Arjan van der Ven4be0e1b2007-05-23 15:50:53 +0000265 if (nr==0)
266 interrupt_0 = delta;
Arjan van der Vend4ef0b92007-05-26 00:02:09 +0000267 else
268 total_interrupt += delta;
Anurag Singhbf459e22011-10-10 10:50:51 -0700269#endif
Arjan van der Ven28615352007-05-11 20:08:05 +0000270 }
271 fclose(file);
272}
273
Arjan van der Ven70551c52008-03-27 03:16:37 +0000274static void read_data_acpi(uint64_t * usage, uint64_t * duration)
Arjan van der Ven28615352007-05-11 20:08:05 +0000275{
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000276 DIR *dir;
Arjan van der Ven28615352007-05-11 20:08:05 +0000277 struct dirent *entry;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000278 FILE *file = NULL;
279 char line[4096];
280 char *c;
281 int clevel = 0;
Arjan van der Ven28615352007-05-11 20:08:05 +0000282
283 memset(usage, 0, 64);
284 memset(duration, 0, 64);
285
286 dir = opendir("/proc/acpi/processor");
287 if (!dir)
288 return;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000289 while ((entry = readdir(dir))) {
290 if (strlen(entry->d_name) < 3)
Arjan van der Ven28615352007-05-11 20:08:05 +0000291 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000292 sprintf(line, "/proc/acpi/processor/%s/power", entry->d_name);
Arjan van der Ven28615352007-05-11 20:08:05 +0000293 file = fopen(line, "r");
294 if (!file)
295 continue;
296
297 clevel = 0;
298
299 while (!feof(file)) {
300 memset(line, 0, 4096);
Arjan van der Venef08f1c2007-05-16 18:28:57 +0000301 if (fgets(line, 4096, file) == NULL)
302 break;
Arjan van der Ven28615352007-05-11 20:08:05 +0000303 c = strstr(line, "age[");
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000304 if (!c)
Arjan van der Ven28615352007-05-11 20:08:05 +0000305 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000306 c += 4;
Arjan van der Venc3ef64b2007-05-18 16:57:34 +0000307 usage[clevel] += 1+strtoull(c, NULL, 10);
Arjan van der Ven28615352007-05-11 20:08:05 +0000308 c = strstr(line, "ation[");
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000309 if (!c)
Arjan van der Ven28615352007-05-11 20:08:05 +0000310 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000311 c += 6;
Arjan van der Ven28615352007-05-11 20:08:05 +0000312 duration[clevel] += strtoull(c, NULL, 10);
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000313
Arjan van der Ven28615352007-05-11 20:08:05 +0000314 clevel++;
Arjan van der Vend252c312007-05-12 18:22:26 +0000315 if (clevel > maxcstate)
316 maxcstate = clevel;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000317
Arjan van der Ven28615352007-05-11 20:08:05 +0000318 }
319 fclose(file);
320 }
321 closedir(dir);
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000322}
Arjan van der Ven28615352007-05-11 20:08:05 +0000323
Arjan van der Ven70551c52008-03-27 03:16:37 +0000324static void read_data_cpuidle(uint64_t * usage, uint64_t * duration)
325{
326 DIR *cpudir;
327 DIR *dir;
328 struct dirent *entry;
329 FILE *file = NULL;
330 char line[4096];
331 char filename[128], *f;
332 int len, clevel = 0;
Patrick Caind5f3cfb2012-10-24 12:33:15 -0700333 int numcpus = 0;
Arjan van der Ven70551c52008-03-27 03:16:37 +0000334
335 memset(usage, 0, 64);
336 memset(duration, 0, 64);
337
338 cpudir = opendir("/sys/devices/system/cpu");
339 if (!cpudir)
340 return;
341
342 /* Loop over cpuN entries */
343 while ((entry = readdir(cpudir))) {
344 if (strlen(entry->d_name) < 3)
345 continue;
346
347 if (!isdigit(entry->d_name[3]))
348 continue;
Patrick Caind5f3cfb2012-10-24 12:33:15 -0700349 len = snprintf(filename, 128, "/sys/devices/system/cpu/%s/online",
350 entry->d_name);
351 file = fopen(filename, "r");
352 if (file) {
353 f = fgets(line, 4096, file);
354 fclose(file);
355 if (f != NULL) {
356 if (*f == '1')
357 numcpus++;
358 else
359 continue;
360 }
361 }
362 len = snprintf(filename, 128, "/sys/devices/system/cpu/%s/cpuidle",
Arjan van der Ven70551c52008-03-27 03:16:37 +0000363 entry->d_name);
364
365 dir = opendir(filename);
366 if (!dir)
367 return;
368
369 clevel = 0;
370
371 /* For each C-state, there is a stateX directory which
372 * contains a 'usage' and a 'time' (duration) file */
373 while ((entry = readdir(dir))) {
374 if (strlen(entry->d_name) < 3)
375 continue;
376 sprintf(filename + len, "/%s/desc", entry->d_name);
377 file = fopen(filename, "r");
Arjan van der Ven160580c2008-03-27 18:16:12 +0000378 if (file) {
Arjan van der Ven70551c52008-03-27 03:16:37 +0000379
Arjan van der Ven160580c2008-03-27 18:16:12 +0000380 memset(line, 0, 4096);
381 f = fgets(line, 4096, file);
382 fclose(file);
383 if (f == NULL)
384 break;
Arjan van der Ven03f2a892008-06-27 17:01:33 +0000385
Arjan van der Ven70551c52008-03-27 03:16:37 +0000386
Arjan van der Ven160580c2008-03-27 18:16:12 +0000387 f = strstr(line, "MWAIT ");
388 if (f) {
389 f += 6;
390 clevel = (strtoull(f, NULL, 16)>>4) + 1;
Arjan van der Ven03f2a892008-06-27 17:01:33 +0000391 sprintf(cnames[clevel], "C%i mwait", clevel);
392 } else
393 sprintf(cnames[clevel], "C%i\t", clevel);
394
Arjan van der Ven160580c2008-03-27 18:16:12 +0000395 f = strstr(line, "POLL IDLE");
396 if (f) {
397 clevel = 0;
Arjan van der Ven03f2a892008-06-27 17:01:33 +0000398 sprintf(cnames[clevel], "%s\t", _("polling"));
Arjan van der Ven160580c2008-03-27 18:16:12 +0000399 }
Arjan van der Ven70551c52008-03-27 03:16:37 +0000400
Arjan van der Ven160580c2008-03-27 18:16:12 +0000401 f = strstr(line, "ACPI HLT");
402 if (f) {
403 clevel = 1;
Arjan van der Ven03f2a892008-06-27 17:01:33 +0000404 sprintf(cnames[clevel], "%s\t", "C1 halt");
Arjan van der Ven160580c2008-03-27 18:16:12 +0000405 }
Arjan van der Ven28e45c62008-03-27 04:37:57 +0000406 }
Arjan van der Ven70551c52008-03-27 03:16:37 +0000407 sprintf(filename + len, "/%s/usage", entry->d_name);
408 file = fopen(filename, "r");
409 if (!file)
410 continue;
411
412 memset(line, 0, 4096);
413 f = fgets(line, 4096, file);
414 fclose(file);
415 if (f == NULL)
416 break;
417
418 usage[clevel] += 1+strtoull(line, NULL, 10);
419
420 sprintf(filename + len, "/%s/time", entry->d_name);
421 file = fopen(filename, "r");
422 if (!file)
423 continue;
424
425 memset(line, 0, 4096);
426 f = fgets(line, 4096, file);
427 fclose(file);
428 if (f == NULL)
429 break;
430
431 duration[clevel] += 1+strtoull(line, NULL, 10);
432
433 clevel++;
434 if (clevel > maxcstate)
435 maxcstate = clevel;
436
437 }
438 closedir(dir);
439
440 }
441 closedir(cpudir);
Patrick Caind5f3cfb2012-10-24 12:33:15 -0700442 if (numcpus)
443 cpu_count = numcpus;
Arjan van der Ven70551c52008-03-27 03:16:37 +0000444}
445
446static void read_data(uint64_t * usage, uint64_t * duration)
447{
448 int r;
449 struct stat s;
450
451 /* Then check for CPUidle */
Arjan van der Ven8569c292008-03-27 14:38:07 +0000452 r = stat("/sys/devices/system/cpu/cpu0/cpuidle", &s);
Arjan van der Ven70551c52008-03-27 03:16:37 +0000453 if (!r) {
454 read_data_cpuidle(usage, duration);
455
456 /* perform residency calculations based on usecs */
457 FREQ = 1000;
458 return;
459 }
460
461 /* First, check for ACPI */
462 r = stat("/proc/acpi/processor", &s);
463 if (!r) {
464 read_data_acpi(usage, duration);
465
466 /* perform residency calculations based on ACPI timer */
467 FREQ = FREQ_ACPI;
468 return;
469 }
470}
471
Arjan van der Ven28615352007-05-11 20:08:05 +0000472void stop_timerstats(void)
473{
474 FILE *file;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000475 file = fopen("/proc/timer_stats", "w");
Arjan van der Ven28615352007-05-11 20:08:05 +0000476 if (!file) {
477 nostats = 1;
478 return;
479 }
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000480 fprintf(file, "0\n");
Arjan van der Ven28615352007-05-11 20:08:05 +0000481 fclose(file);
482}
483void start_timerstats(void)
484{
485 FILE *file;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000486 file = fopen("/proc/timer_stats", "w");
Arjan van der Ven28615352007-05-11 20:08:05 +0000487 if (!file) {
488 nostats = 1;
489 return;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000490 }
491 fprintf(file, "1\n");
Arjan van der Ven28615352007-05-11 20:08:05 +0000492 fclose(file);
493}
494
Anurag Singhc5781f82011-10-10 10:44:44 -0700495void reset_msm_pm_stats(void)
496{
497 FILE *file;
498 file = fopen("/proc/msm_pm_stats", "w");
499 if (!file)
500 return;
501 fprintf(file, "reset\n");
502 fclose(file);
503}
504
505
Arjan van der Ven6473da42007-05-13 15:23:31 +0000506int line_compare (const void *av, const void *bv)
507{
508 const struct line *a = av, *b = bv;
509 return b->count - a->count;
510}
511
Arjan van der Ven28615352007-05-11 20:08:05 +0000512void sort_lines(void)
513{
Arjan van der Ven6473da42007-05-13 15:23:31 +0000514 qsort (lines, linehead, sizeof (struct line), line_compare);
Arjan van der Ven28615352007-05-11 20:08:05 +0000515}
516
Arjan van der Vena90b3522008-03-27 03:52:28 +0000517
518
Auke Kok41e83602008-09-04 19:59:19 +0000519int print_battery_proc_acpi(void)
Arjan van der Ven28615352007-05-11 20:08:05 +0000520{
521 DIR *dir;
522 struct dirent *dirent;
523 FILE *file;
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000524 double rate = 0;
525 double cap = 0;
Arjan van der Ven28615352007-05-11 20:08:05 +0000526
527 char filename[256];
528
529 dir = opendir("/proc/acpi/battery");
530 if (!dir)
Arjan van der Ven160580c2008-03-27 18:16:12 +0000531 return 0;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000532
533 while ((dirent = readdir(dir))) {
Arjan van der Venb3cd4df2007-05-14 18:18:32 +0000534 int dontcount = 0;
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000535 double voltage = 0.0;
536 double amperes_drawn = 0.0;
537 double watts_drawn = 0.0;
538 double amperes_left = 0.0;
539 double watts_left = 0.0;
Arjan van der Ven36382622007-05-17 17:40:55 +0000540 char line[1024];
Arjan van der Ven740ee8f2007-05-16 15:30:01 +0000541
542 if (strlen(dirent->d_name) < 3)
543 continue;
544
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000545 sprintf(filename, "/proc/acpi/battery/%s/state", dirent->d_name);
Arjan van der Ven28615352007-05-11 20:08:05 +0000546 file = fopen(filename, "r");
547 if (!file)
548 continue;
Arjan van der Ven6a0ade22007-05-24 23:45:33 +0000549 memset(line, 0, 1024);
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000550 while (fgets(line, 1024, file) != NULL) {
Arjan van der Ven28615352007-05-11 20:08:05 +0000551 char *c;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000552 if (strstr(line, "present:") && strstr(line, "no"))
Arjan van der Ven28615352007-05-11 20:08:05 +0000553 break;
554
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000555 if (strstr(line, "charging state:")
556 && !strstr(line, "discharging"))
557 dontcount = 1;
Arjan van der Ven28615352007-05-11 20:08:05 +0000558 c = strchr(line, ':');
559 if (!c)
560 continue;
561 c++;
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000562
563 if (strstr(line, "present voltage"))
564 voltage = strtoull(c, NULL, 10) / 1000.0;
565
566 if (strstr(line, "remaining capacity") && strstr(c, "mW"))
Arjan van der Ven880b2cb2007-05-14 22:00:48 +0000567 watts_left = strtoull(c, NULL, 10) / 1000.0;
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000568
569 if (strstr(line, "remaining capacity") && strstr(c, "mAh"))
Arjan van der Ven880b2cb2007-05-14 22:00:48 +0000570 amperes_left = strtoull(c, NULL, 10) / 1000.0;
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000571
572 if (strstr(line, "present rate") && strstr(c, "mW"))
Arjan van der Ven880b2cb2007-05-14 22:00:48 +0000573 watts_drawn = strtoull(c, NULL, 10) / 1000.0 ;
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000574
575 if (strstr(line, "present rate") && strstr(c, "mA"))
Arjan van der Ven880b2cb2007-05-14 22:00:48 +0000576 amperes_drawn = strtoull(c, NULL, 10) / 1000.0;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000577
Arjan van der Ven28615352007-05-11 20:08:05 +0000578 }
579 fclose(file);
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000580
581 if (!dontcount) {
582 rate += watts_drawn + voltage * amperes_drawn;
583 }
584 cap += watts_left + voltage * amperes_left;
Arjan van der Ven2e74d1d2007-05-26 15:47:40 +0000585
Arjan van der Ven11d9a2e2007-05-14 21:55:31 +0000586
Arjan van der Ven28615352007-05-11 20:08:05 +0000587 }
588 closedir(dir);
Arjan van der Ven387e2912007-06-06 22:32:18 +0000589 if (prev_bat_cap - cap < 0.001 && rate < 0.001)
590 last_bat_time = 0;
Arjan van der Ven6435af22007-05-26 16:08:03 +0000591 if (!last_bat_time) {
592 last_bat_time = prev_bat_time = time(NULL);
593 last_bat_cap = prev_bat_cap = cap;
594 }
Arjan van der Ven128f5952007-05-26 16:14:29 +0000595 if (time(NULL) - last_bat_time >= 400) {
Arjan van der Ven6435af22007-05-26 16:08:03 +0000596 prev_bat_cap = last_bat_cap;
597 prev_bat_time = last_bat_time;
598 last_bat_time = time(NULL);
599 last_bat_cap = cap;
600 }
Arjan van der Ven399ceb52007-05-27 02:15:12 +0000601
Arjan van der Vene0e45f12007-05-26 15:56:29 +0000602 show_acpi_power_line(rate, cap, prev_bat_cap - cap, time(NULL) - prev_bat_time);
Arjan van der Ven160580c2008-03-27 18:16:12 +0000603 return 1;
Arjan van der Ven28615352007-05-11 20:08:05 +0000604}
605
Auke Kok41e83602008-09-04 19:59:19 +0000606int print_battery_proc_pmu(void)
607{
608 char line[80];
609 int i;
610 int power_present = 0;
611 int num_batteries = 0;
612 /* unsigned rem_time_sec = 0; */
613 unsigned charge_mAh = 0, max_charge_mAh = 0, voltage_mV = 0;
614 int discharge_mA = 0;
615 FILE *fd;
616
617 fd = fopen("/proc/pmu/info", "r");
618 if (fd == NULL)
619 return 0;
620
621 while ( fgets(line, sizeof(line), fd) != NULL )
622 {
623 if (strncmp("AC Power", line, strlen("AC Power")) == 0)
624 sscanf(strchr(line, ':')+2, "%d", &power_present);
625 else if (strncmp("Battery count", line, strlen("Battery count")) == 0)
626 sscanf(strchr(line, ':')+2, "%d", &num_batteries);
627 }
628 fclose(fd);
629
630 for (i = 0; i < num_batteries; ++i)
631 {
632 char file_name[20];
633 int flags = 0;
634 /* int battery_charging, battery_full; */
635 /* unsigned this_rem_time_sec = 0; */
636 unsigned this_charge_mAh = 0, this_max_charge_mAh = 0;
637 unsigned this_voltage_mV = 0, this_discharge_mA = 0;
638
639 snprintf(file_name, sizeof(file_name), "/proc/pmu/battery_%d", i);
640 fd = fopen(file_name, "r");
641 if (fd == NULL)
642 continue;
643
644 while (fgets(line, sizeof(line), fd) != NULL)
645 {
646 if (strncmp("flags", line, strlen("flags")) == 0)
647 sscanf(strchr(line, ':')+2, "%x", &flags);
648 else if (strncmp("charge", line, strlen("charge")) == 0)
649 sscanf(strchr(line, ':')+2, "%d", &this_charge_mAh);
650 else if (strncmp("max_charge", line, strlen("max_charge")) == 0)
651 sscanf(strchr(line, ':')+2, "%d", &this_max_charge_mAh);
652 else if (strncmp("voltage", line, strlen("voltage")) == 0)
653 sscanf(strchr(line, ':')+2, "%d", &this_voltage_mV);
654 else if (strncmp("current", line, strlen("current")) == 0)
655 sscanf(strchr(line, ':')+2, "%d", &this_discharge_mA);
656 /* else if (strncmp("time rem.", line, strlen("time rem.")) == 0) */
657 /* sscanf(strchr(line, ':')+2, "%d", &this_rem_time_sec); */
658 }
659 fclose(fd);
660
661 if ( !(flags & 0x1) )
662 /* battery isn't present */
663 continue;
664
665 /* battery_charging = flags & 0x2; */
666 /* battery_full = !battery_charging && power_present; */
667
668 charge_mAh += this_charge_mAh;
669 max_charge_mAh += this_max_charge_mAh;
670 voltage_mV += this_voltage_mV;
671 discharge_mA += this_discharge_mA;
672 /* rem_time_sec += this_rem_time_sec; */
673 }
Auke Kok07ed39e2008-09-16 17:22:14 +0000674 show_pmu_power_line(voltage_mV, charge_mAh, max_charge_mAh,
Auke Kok41e83602008-09-04 19:59:19 +0000675 discharge_mA);
676 return 1;
677}
678
Arjan van der Vena90b3522008-03-27 03:52:28 +0000679void print_battery_sysfs(void)
680{
681 DIR *dir;
682 struct dirent *dirent;
683 FILE *file;
684 double rate = 0;
685 double cap = 0;
686
687 char filename[256];
Arjan van der Ven160580c2008-03-27 18:16:12 +0000688
Auke Kok41e83602008-09-04 19:59:19 +0000689 if (print_battery_proc_acpi())
690 return;
691
692 if (print_battery_proc_pmu())
Arjan van der Ven160580c2008-03-27 18:16:12 +0000693 return;
Arjan van der Vena90b3522008-03-27 03:52:28 +0000694
695 dir = opendir("/sys/class/power_supply");
696 if (!dir) {
Arjan van der Vena90b3522008-03-27 03:52:28 +0000697 return;
698 }
699
700 while ((dirent = readdir(dir))) {
701 int dontcount = 0;
702 double voltage = 0.0;
703 double amperes_drawn = 0.0;
704 double watts_drawn = 0.0;
705 double watts_left = 0.0;
706 char line[1024];
707
708 if (strstr(dirent->d_name, "AC"))
709 continue;
710
711 sprintf(filename, "/sys/class/power_supply/%s/present", dirent->d_name);
712 file = fopen(filename, "r");
713 if (!file)
714 continue;
715 int s;
716 if ((s = getc(file)) != EOF) {
717 if (s == 0)
718 break;
719 }
720 fclose(file);
721
722 sprintf(filename, "/sys/class/power_supply/%s/status", dirent->d_name);
723 file = fopen(filename, "r");
724 if (!file)
725 continue;
726 memset(line, 0, 1024);
727 if (fgets(line, 1024, file) != NULL) {
728 if (!strstr(line, "Discharging"))
729 dontcount = 1;
730 }
731 fclose(file);
732
733 sprintf(filename, "/sys/class/power_supply/%s/voltage_now", dirent->d_name);
734 file = fopen(filename, "r");
735 if (!file)
736 continue;
737 memset(line, 0, 1024);
738 if (fgets(line, 1024, file) != NULL) {
739 voltage = strtoull(line, NULL, 10) / 1000000.0;
740 }
741 fclose(file);
742
743 sprintf(filename, "/sys/class/power_supply/%s/energy_now", dirent->d_name);
744 file = fopen(filename, "r");
Auke Kok07ed39e2008-09-16 17:22:14 +0000745 watts_left = 1;
746 if (!file) {
747 sprintf(filename, "/sys/class/power_supply/%s/charge_now", dirent->d_name);
748 file = fopen(filename, "r");
749 if (!file)
750 continue;
751
752 /* W = A * V */
753 watts_left = voltage;
Arjan van der Vena90b3522008-03-27 03:52:28 +0000754 }
Auke Kok07ed39e2008-09-16 17:22:14 +0000755 memset(line, 0, 1024);
756 if (fgets(line, 1024, file) != NULL)
757 watts_left *= strtoull(line, NULL, 10) / 1000000.0;
Arjan van der Vena90b3522008-03-27 03:52:28 +0000758 fclose(file);
759
760 sprintf(filename, "/sys/class/power_supply/%s/current_now", dirent->d_name);
761 file = fopen(filename, "r");
762 if (!file)
763 continue;
764 memset(line, 0, 1024);
765 if (fgets(line, 1024, file) != NULL) {
Auke Kok07ed39e2008-09-16 17:22:14 +0000766 amperes_drawn = strtoull(line, NULL, 10) / 1000000.0;
Arjan van der Vena90b3522008-03-27 03:52:28 +0000767 }
768 fclose(file);
769
770 if (!dontcount) {
771 rate += watts_drawn + voltage * amperes_drawn;
772 }
773 cap += watts_left;
774
775
776 }
777 closedir(dir);
778 if (prev_bat_cap - cap < 0.001 && rate < 0.001)
779 last_bat_time = 0;
780 if (!last_bat_time) {
781 last_bat_time = prev_bat_time = time(NULL);
782 last_bat_cap = prev_bat_cap = cap;
783 }
784 if (time(NULL) - last_bat_time >= 400) {
785 prev_bat_cap = last_bat_cap;
786 prev_bat_time = last_bat_time;
787 last_bat_time = time(NULL);
788 last_bat_cap = cap;
789 }
790
791 show_acpi_power_line(rate, cap, prev_bat_cap - cap, time(NULL) - prev_bat_time);
792}
793
Arjan van der Ven70551c52008-03-27 03:16:37 +0000794char cstate_lines[12][200];
Arjan van der Venbdedb3c2007-05-22 23:53:28 +0000795
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000796void usage()
797{
798 printf(_("Usage: powertop [OPTION...]\n"));
799 printf(_(" -d, --dump read wakeups once and print list of top offenders\n"));
800 printf(_(" -t, --time=DOUBLE default time to gather data in seconds\n"));
Anurag Singhc5781f82011-10-10 10:44:44 -0700801 printf(_(" -r, --reset Reset PM stats data\n"));
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000802 printf(_(" -h, --help Show this help message\n"));
Auke Kokeecdec52008-12-01 18:22:47 +0000803 printf(_(" -v, --version Show version information and exit\n"));
804 exit(0);
805}
806
807void version()
808{
Auke Kokfb604782008-12-11 21:35:46 +0000809 printf(_("powertop version %s\n"), VERSION);
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000810 exit(0);
811}
812
Arjan van der Ven28615352007-05-11 20:08:05 +0000813int main(int argc, char **argv)
814{
815 char line[1024];
Arjan van der Venbdedb3c2007-05-22 23:53:28 +0000816 int ncursesinited=0;
Arjan van der Ven28615352007-05-11 20:08:05 +0000817 FILE *file = NULL;
818 uint64_t cur_usage[8], cur_duration[8];
Arjan van der Vend1d13382007-05-26 21:24:16 +0000819 double wakeups_per_second = 0;
820
Arjan van der Venb517d0f2007-08-19 17:39:51 +0000821 setlocale (LC_ALL, "");
822 bindtextdomain ("powertop", "/usr/share/locale");
823 textdomain ("powertop");
Arjan van der Venfe2d7152007-06-23 04:40:40 +0000824
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000825 while (1) {
826 static struct option opts[] = {
827 { "dump", 0, NULL, 'd' },
828 { "time", 1, NULL, 't' },
Anurag Singhc5781f82011-10-10 10:44:44 -0700829 { "reset", 0, NULL, 'r' },
830 { "pids", 0, NULL, 'p' },
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000831 { "help", 0, NULL, 'h' },
Auke Kokeecdec52008-12-01 18:22:47 +0000832 { "version", 0, NULL, 'v' },
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000833 { 0, 0, NULL, 0 }
834 };
835 int index2 = 0, c;
836
Anurag Singhc5781f82011-10-10 10:44:44 -0700837 c = getopt_long(argc, argv, "dt:rphv", opts, &index2);
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000838 if (c == -1)
839 break;
840 switch (c) {
841 case 'd':
842 dump = 1;
843 break;
844 case 't':
845 ticktime = strtod(optarg, NULL);
846 break;
Anurag Singhc5781f82011-10-10 10:44:44 -0700847 case 'r':
848 reset_pm_stats = 1;
849 break;
850 case 'p':
851 showpids = 1;
852 break;
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000853 case 'h':
854 usage();
855 break;
Auke Kokeecdec52008-12-01 18:22:47 +0000856 case 'v':
857 version();
858 break;
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000859 default:
860 ;
861 }
862 }
Arjan van der Ven28615352007-05-11 20:08:05 +0000863
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000864 if (!dump)
865 ticktime = 5.0;
866
Arjan van der Ven5683ee32007-07-28 03:33:35 +0000867 system("/sbin/modprobe cpufreq_stats &> /dev/null");
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000868 read_data(&start_usage[0], &start_duration[0]);
Arjan van der Ven329bb7a2007-06-17 06:27:18 +0000869
Arjan van der Vene967a402007-05-22 04:14:59 +0000870
Arjan van der Ven28615352007-05-11 20:08:05 +0000871 memcpy(last_usage, start_usage, sizeof(last_usage));
872 memcpy(last_duration, start_duration, sizeof(last_duration));
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000873
Arjan van der Ven28615352007-05-11 20:08:05 +0000874 do_proc_irq();
Arjan van der Ven825ec692007-05-24 17:41:06 +0000875 do_proc_irq();
Arjan van der Ven5103dd72007-06-17 16:27:44 +0000876 do_cpufreq_stats();
Arjan van der Ven9fc3a162007-10-03 20:09:00 +0000877 count_usb_urbs();
Arjan van der Veneeb2e172008-03-27 04:01:21 +0000878 count_usb_urbs();
Arjan van der Ven28615352007-05-11 20:08:05 +0000879
880 memset(cur_usage, 0, sizeof(cur_usage));
881 memset(cur_duration, 0, sizeof(cur_duration));
Auke Kokeecdec52008-12-01 18:22:47 +0000882 printf("PowerTOP " VERSION " (C) 2007, 2008 Intel Corporation \n\n");
Arjan van der Ven34640a42007-08-19 17:42:57 +0000883 if (geteuid() != 0)
Arjan van der Venc425d822007-05-22 03:53:34 +0000884 printf(_("PowerTOP needs to be run as root to collect enough information\n"));
Arjan van der Ven735c0482007-05-28 17:51:37 +0000885 printf(_("Collecting data for %i seconds \n"), (int)ticktime);
Arjan van der Vendb0370a2008-03-27 18:44:50 +0000886 printf("\n\n");
887 print_intel_cstates();
Arjan van der Ven28615352007-05-11 20:08:05 +0000888 stop_timerstats();
Arjan van der Venbdedb3c2007-05-22 23:53:28 +0000889
Arjan van der Ven28615352007-05-11 20:08:05 +0000890 while (1) {
891 double maxsleep = 0.0;
892 int64_t totalticks;
893 int64_t totalevents;
Arjan van der Ven41cc9942007-05-24 20:20:19 +0000894 fd_set rfds;
895 struct timeval tv;
896 int key;
897
Patrick Caind5f3cfb2012-10-24 12:33:15 -0700898 int num_cpus = sysconf(_SC_NPROCESSORS_ONLN);
Arjan van der Ven28615352007-05-11 20:08:05 +0000899 int i = 0;
900 double c0 = 0;
901 char *c;
Arjan van der Ven41cc9942007-05-24 20:20:19 +0000902
903
904 FD_ZERO(&rfds);
Anurag Singhc5781f82011-10-10 10:44:44 -0700905#ifndef NO_NCURSES
906 /* Do not check for stdin (fd: 0)
907 * This would cause select to return immediately
908 * when the USB is re-attached on ADB shell.
909 */
Arjan van der Ven41cc9942007-05-24 20:20:19 +0000910 FD_SET(0, &rfds);
Anurag Singhc5781f82011-10-10 10:44:44 -0700911#endif
Arjan van der Ven41cc9942007-05-24 20:20:19 +0000912 tv.tv_sec = ticktime;
Arjan van der Ven735c0482007-05-28 17:51:37 +0000913 tv.tv_usec = (ticktime - tv.tv_sec) * 1000000;;
Anurag Singhc5781f82011-10-10 10:44:44 -0700914 if (reset_pm_stats)
915 reset_msm_pm_stats();
Arjan van der Ven28615352007-05-11 20:08:05 +0000916 do_proc_irq();
917 start_timerstats();
Arjan van der Ven41cc9942007-05-24 20:20:19 +0000918
919
920 key = select(1, &rfds, NULL, NULL, &tv);
921
Arjan van der Ven735c0482007-05-28 17:51:37 +0000922 if (key && tv.tv_sec) ticktime = ticktime - tv.tv_sec - tv.tv_usec/1000000.0;
Arjan van der Venb43056b2007-05-24 20:47:42 +0000923
Arjan van der Venfcddf6e2007-06-23 05:33:16 +0000924
Arjan van der Ven28615352007-05-11 20:08:05 +0000925 stop_timerstats();
Anurag Singhc5781f82011-10-10 10:44:44 -0700926 msm_pm_stats();
Arjan van der Vene0399122007-05-24 14:56:27 +0000927 clear_lines();
928 do_proc_irq();
Arjan van der Ven28615352007-05-11 20:08:05 +0000929 read_data(&cur_usage[0], &cur_duration[0]);
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000930
Arjan van der Ven28615352007-05-11 20:08:05 +0000931 totalticks = 0;
932 totalevents = 0;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000933 for (i = 0; i < 8; i++)
Arjan van der Ven28615352007-05-11 20:08:05 +0000934 if (cur_usage[i]) {
935 totalticks += cur_duration[i] - last_duration[i];
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000936 totalevents += cur_usage[i] - last_usage[i];
Arjan van der Ven28615352007-05-11 20:08:05 +0000937 }
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000938
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000939 if (!dump) {
940 if (!ncursesinited) {
941 initialize_curses();
942 ncursesinited++;
943 }
944 setup_windows();
945 show_title_bar();
Arjan van der Venbdedb3c2007-05-22 23:53:28 +0000946 }
Arjan van der Venbdedb3c2007-05-22 23:53:28 +0000947
948 memset(&cstate_lines, 0, sizeof(cstate_lines));
Arjan van der Ven5288ca72007-05-23 16:24:06 +0000949 topcstate = -4;
Arjan van der Venbbd7aee2007-06-23 05:05:38 +0000950 if (totalevents == 0 && maxcstate <= 1) {
Arjan van der Ven70551c52008-03-27 03:16:37 +0000951 sprintf(cstate_lines[5],_("< Detailed C-state information is not available.>\n"));
Arjan van der Ven28615352007-05-11 20:08:05 +0000952 } else {
Patrick Caind5f3cfb2012-10-24 12:33:15 -0700953 double sleept, percentage;
954 if (cpu_count)
955 num_cpus = cpu_count;
956 c0 = num_cpus * ticktime * 1000 * FREQ - totalticks;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000957 if (c0 < 0)
958 c0 = 0; /* rounding errors in measurement might make c0 go slightly negative.. this is confusing */
Arjan van der Ven6fe1c572007-08-18 21:52:07 +0000959 sprintf(cstate_lines[0], _("Cn\t Avg residency\n"));
Arjan van der Ven02a9e362007-06-23 05:15:45 +0000960
Patrick Caind5f3cfb2012-10-24 12:33:15 -0700961 percentage = c0 * 100.0 / (num_cpus * ticktime * 1000 * FREQ);
Arjan van der Ven02a9e362007-06-23 05:15:45 +0000962 sprintf(cstate_lines[1], _("C0 (cpu running) (%4.1f%%)\n"), percentage);
963 if (percentage > 50)
964 topcstate = 0;
Arjan van der Ven70551c52008-03-27 03:16:37 +0000965 for (i = 0; i < 8; i++)
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000966 if (cur_usage[i]) {
Arjan van der Ven00742892007-06-01 03:16:51 +0000967 sleept = (cur_duration[i] - last_duration[i]) / (cur_usage[i] - last_usage[i]
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000968 + 0.1) / FREQ;
Arjan van der Ven5288ca72007-05-23 16:24:06 +0000969 percentage = (cur_duration[i] -
970 last_duration[i]) * 100 /
Patrick Caind5f3cfb2012-10-24 12:33:15 -0700971 (num_cpus * ticktime * 1000 * FREQ);
Arjan van der Ven70551c52008-03-27 03:16:37 +0000972
973 if (cnames[i][0]==0)
974 sprintf(cnames[i],"C%i",i+1);
Arjan van der Venbdedb3c2007-05-22 23:53:28 +0000975 sprintf
Arjan van der Ven03f2a892008-06-27 17:01:33 +0000976 (cstate_lines[2+i], _("%s\t%5.1fms (%4.1f%%)\n"),
Arjan van der Ven70551c52008-03-27 03:16:37 +0000977 cnames[i], sleept, percentage);
Arjan van der Ven00742892007-06-01 03:16:51 +0000978 if (maxsleep < sleept)
979 maxsleep = sleept;
Arjan van der Ven5288ca72007-05-23 16:24:06 +0000980 if (percentage > 50)
981 topcstate = i+1;
982
Arjan van der Ven28615352007-05-11 20:08:05 +0000983 }
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000984 }
Arjan van der Ven329bb7a2007-06-17 06:27:18 +0000985 do_cpufreq_stats();
Arjan van der Venbdedb3c2007-05-22 23:53:28 +0000986 show_cstates();
Anurag Singhc5781f82011-10-10 10:44:44 -0700987 show_msm_pm_stats();
Arjan van der Ven28615352007-05-11 20:08:05 +0000988 /* now the timer_stats info */
Arjan van der Ven12e55ca2007-05-12 22:52:54 +0000989 memset(line, 0, sizeof(line));
Arjan van der Ven28615352007-05-11 20:08:05 +0000990 totalticks = 0;
Arjan van der Ven7dd38632007-06-17 16:03:15 +0000991 file = NULL;
Arjan van der Ven28615352007-05-11 20:08:05 +0000992 if (!nostats)
Arjan van der Ven6473da42007-05-13 15:23:31 +0000993 file = fopen("/proc/timer_stats", "r");
994 while (file && !feof(file)) {
Arjan van der Ven28615352007-05-11 20:08:05 +0000995 char *count, *pid, *process, *func;
Arjan van der Ven91793a82007-05-25 15:47:52 +0000996 char line2[1024];
Arjan van der Ven28615352007-05-11 20:08:05 +0000997 int cnt;
Arjan van der Ven5683ee32007-07-28 03:33:35 +0000998 int deferrable = 0;
Arjan van der Ven6a0ade22007-05-24 23:45:33 +0000999 memset(line, 0, 1024);
Arjan van der Venef08f1c2007-05-16 18:28:57 +00001000 if (fgets(line, 1024, file) == NULL)
1001 break;
Arjan van der Ven28615352007-05-11 20:08:05 +00001002 if (strstr(line, "total events"))
1003 break;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001004 c = count = &line[0];
1005 c = strchr(c, ',');
Arjan van der Ven28615352007-05-11 20:08:05 +00001006 if (!c)
1007 continue;
1008 *c = 0;
1009 c++;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001010 while (*c != 0 && *c == ' ')
1011 c++;
Arjan van der Ven28615352007-05-11 20:08:05 +00001012 pid = c;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001013 c = strchr(c, ' ');
Arjan van der Ven28615352007-05-11 20:08:05 +00001014 if (!c)
1015 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001016 *c = 0;
1017 c++;
1018 while (*c != 0 && *c == ' ')
1019 c++;
Arjan van der Ven28615352007-05-11 20:08:05 +00001020 process = c;
1021 c = strchr(c, ' ');
1022 if (!c)
1023 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001024 *c = 0;
1025 c++;
1026 while (*c != 0 && *c == ' ')
1027 c++;
Arjan van der Ven28615352007-05-11 20:08:05 +00001028 func = c;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001029 if (strcmp(process, "insmod") == 0)
Arjan van der Ven85120542007-10-29 17:49:28 +00001030 process = _("<kernel module>");
Arjan van der Venefcad812007-05-25 15:21:38 +00001031 if (strcmp(process, "modprobe") == 0)
Arjan van der Ven85120542007-10-29 17:49:28 +00001032 process = _("<kernel module>");
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001033 if (strcmp(process, "swapper") == 0)
Arjan van der Ven85120542007-10-29 17:49:28 +00001034 process = _("<kernel core>");
Arjan van der Ven28615352007-05-11 20:08:05 +00001035 c = strchr(c, '\n');
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001036 if (strncmp(func, "tick_nohz_", 10) == 0)
Arjan van der Ven28615352007-05-11 20:08:05 +00001037 continue;
Arjan van der Vendccde0d2007-06-11 19:46:49 +00001038 if (strncmp(func, "tick_setup_sched_timer", 20) == 0)
1039 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001040 if (strcmp(process, "powertop") == 0)
Arjan van der Ven28615352007-05-11 20:08:05 +00001041 continue;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001042 if (c)
1043 *c = 0;
Arjan van der Ven5683ee32007-07-28 03:33:35 +00001044 cnt = strtoull(count, &c, 10);
1045 while (*c != 0) {
1046 if (*c++ == 'D')
1047 deferrable = 1;
1048 }
1049 if (deferrable)
1050 continue;
Arjan van der Ven91793a82007-05-25 15:47:52 +00001051 sprintf(line2, "%15s : %s", process, func);
Arjan van der Ven356cd2c2007-12-13 21:52:01 +00001052 push_line_pid(line2, cnt, pid);
Arjan van der Ven28615352007-05-11 20:08:05 +00001053 }
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001054 if (file)
Arjan van der Ven28615352007-05-11 20:08:05 +00001055 pclose(file);
Anurag Singhbf459e22011-10-10 10:50:51 -07001056#ifdef PLATFORM_NO_INT0
1057 totalevents = total_interrupt;
1058#endif
Arjan van der Ven4be0e1b2007-05-23 15:50:53 +00001059 if (strstr(line, "total events")) {
1060 int d;
Patrick Caind5f3cfb2012-10-24 12:33:15 -07001061 d = strtoull(line, NULL, 10) / num_cpus;
Arjan van der Vene772a472007-05-26 00:03:31 +00001062 if (totalevents == 0) { /* No c-state info available, use timerstats instead */
Patrick Caind5f3cfb2012-10-24 12:33:15 -07001063 totalevents = d * num_cpus + total_interrupt;
Arjan van der Vene772a472007-05-26 00:03:31 +00001064 if (d < interrupt_0)
1065 totalevents += interrupt_0 - d;
1066 }
Arjan van der Ven4be0e1b2007-05-23 15:50:53 +00001067 if (d>0 && d < interrupt_0)
Arjan van der Ven85120542007-10-29 17:49:28 +00001068 push_line(_(" <interrupt> : extra timer interrupt"), interrupt_0 - d);
Arjan van der Ven4be0e1b2007-05-23 15:50:53 +00001069 }
1070
Arjan van der Venbdedb3c2007-05-22 23:53:28 +00001071
1072 if (totalevents && ticktime) {
Patrick Caind5f3cfb2012-10-24 12:33:15 -07001073 wakeups_per_second = totalevents * 1.0 / ticktime / num_cpus;
1074 show_wakeups(wakeups_per_second, ticktime, c0 * 100.0 / (num_cpus * ticktime * 1000 * FREQ));
Arjan van der Ven28615352007-05-11 20:08:05 +00001075 }
Arjan van der Ven9fc3a162007-10-03 20:09:00 +00001076 count_usb_urbs();
Arjan van der Vena90b3522008-03-27 03:52:28 +00001077 print_battery_sysfs();
Arjan van der Ven28615352007-05-11 20:08:05 +00001078 count_lines();
Arjan van der Venbdedb3c2007-05-22 23:53:28 +00001079 sort_lines();
Arjan van der Ven329bb7a2007-06-17 06:27:18 +00001080
Arjan van der Ven2b7db8e2007-06-23 19:03:34 +00001081 displaytime = displaytime - ticktime;
1082
Arjan van der Venbdedb3c2007-05-22 23:53:28 +00001083 show_timerstats(nostats, ticktime);
1084
Arjan van der Ven28615352007-05-11 20:08:05 +00001085 if (maxsleep < 5.0)
Arjan van der Ven28615352007-05-11 20:08:05 +00001086 ticktime = 10;
Arjan van der Venefa67c22007-06-07 18:09:04 +00001087 else if (maxsleep < 30.0)
1088 ticktime = 15;
Arjan van der Ven28615352007-05-11 20:08:05 +00001089 else if (maxsleep < 100.0)
1090 ticktime = 20;
1091 else if (maxsleep < 400.0)
1092 ticktime = 30;
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001093 else
Arjan van der Ven28615352007-05-11 20:08:05 +00001094 ticktime = 45;
1095
Anurag Singhc5781f82011-10-10 10:44:44 -07001096
Arjan van der Vend353b412007-05-25 21:22:14 +00001097 if (key) {
1098 char keychar;
Auke Koke0f67662008-11-26 19:07:58 +00001099 int keystroke = fgetc(stdin);
Anurag Singhc5781f82011-10-10 10:44:44 -07001100#ifndef NO_NCURSES
1101 /* Do not handle EOF when not using ncurses as
1102 * the shell would pass the EOF to the app causing
1103 * it to stop.
1104 */
Auke Koke0f67662008-11-26 19:07:58 +00001105 if (keystroke == EOF)
1106 exit(EXIT_SUCCESS);
Anurag Singhc5781f82011-10-10 10:44:44 -07001107#endif
Arjan van der Vend353b412007-05-25 21:22:14 +00001108
Auke Koke0f67662008-11-26 19:07:58 +00001109 keychar = toupper(keystroke);
Arjan van der Vend353b412007-05-25 21:22:14 +00001110 if (keychar == 'Q')
1111 exit(EXIT_SUCCESS);
1112 if (keychar == 'R')
1113 ticktime = 3;
Anurag Singhc5781f82011-10-10 10:44:44 -07001114#ifndef NO_SUGGESTIONS
Arjan van der Vend353b412007-05-25 21:22:14 +00001115 if (keychar == suggestion_key && suggestion_activate) {
1116 suggestion_activate();
1117 ticktime = 2;
Arjan van der Ven2b7db8e2007-06-23 19:03:34 +00001118 displaytime = -1.0;
Arjan van der Ven356cd2c2007-12-13 21:52:01 +00001119 } else
Anurag Singhc5781f82011-10-10 10:44:44 -07001120#endif
Arjan van der Ven356cd2c2007-12-13 21:52:01 +00001121 if (keychar == 'P')
1122 showpids = !showpids;
Anurag Singhc5781f82011-10-10 10:44:44 -07001123
Arjan van der Vend353b412007-05-25 21:22:14 +00001124 }
1125
Arjan van der Ven96f61202007-06-05 01:46:09 +00001126 if (wakeups_per_second < 0)
1127 ticktime = 2;
1128
Anurag Singhc5781f82011-10-10 10:44:44 -07001129
1130#ifndef NO_SUGGESTIONS
Arjan van der Ven825ec692007-05-24 17:41:06 +00001131 reset_suggestions();
Arjan van der Vendb82c2b2007-05-22 23:56:20 +00001132
Arjan van der Ven1b730c82007-05-18 01:59:28 +00001133 suggest_kernel_config("CONFIG_USB_SUSPEND", 1,
Arjan van der Ven825ec692007-05-24 17:41:06 +00001134 _("Suggestion: Enable the CONFIG_USB_SUSPEND kernel configuration option.\nThis option will automatically disable UHCI USB when not in use, and may\nsave approximately 1 Watt of power."), 20);
Arjan van der Vendb16ceb2007-05-13 16:43:34 +00001135 suggest_kernel_config("CONFIG_CPU_FREQ_GOV_ONDEMAND", 1,
Arjan van der Venc425d822007-05-22 03:53:34 +00001136 _("Suggestion: Enable the CONFIG_CPU_FREQ_GOV_ONDEMAND kernel configuration option.\n"
Arjan van der Ven2889d762007-05-26 14:13:41 +00001137 "The 'ondemand' CPU speed governor will minimize the CPU power usage while\n" "giving you performance when it is needed."), 5);
Arjan van der Ven825ec692007-05-24 17:41:06 +00001138 suggest_kernel_config("CONFIG_NO_HZ", 1, _("Suggestion: Enable the CONFIG_NO_HZ kernel configuration option.\nThis option is required to get any kind of longer sleep times in the CPU."), 50);
Arjan van der Vend6e336b2007-06-01 07:16:43 +00001139 suggest_kernel_config("CONFIG_ACPI_BATTERY", 1, _("Suggestion: Enable the CONFIG_ACPI_BATTERY kernel configuration option.\n "
1140 "This option is required to get power estimages from PowerTOP"), 5);
Arjan van der Ven1666e8d2007-05-15 23:37:44 +00001141 suggest_kernel_config("CONFIG_HPET_TIMER", 1,
Arjan van der Venc350a1b2007-06-08 21:06:39 +00001142 _("Suggestion: Enable the CONFIG_HPET_TIMER kernel configuration option.\n"
Arjan van der Vene0c35c42007-06-04 05:13:39 +00001143 "Without HPET support the kernel needs to wake up every 20 milliseconds for \n" "some housekeeping tasks."), 10);
Arjan van der Ven26a37712007-05-26 14:11:47 +00001144 if (!access("/sys/module/snd_ac97_codec", F_OK) &&
1145 access("/sys/module/snd_ac97_codec/parameters/power_save", F_OK))
Arjan van der Ven0024c932007-05-23 16:27:03 +00001146 suggest_kernel_config("CONFIG_SND_AC97_POWER_SAVE", 1,
Arjan van der Venc425d822007-05-22 03:53:34 +00001147 _("Suggestion: Enable the CONFIG_SND_AC97_POWER_SAVE kernel configuration option.\n"
Arjan van der Ven1b730c82007-05-18 01:59:28 +00001148 "This option will automatically power down your sound codec when not in use,\n"
Arjan van der Ven825ec692007-05-24 17:41:06 +00001149 "and can save approximately half a Watt of power."), 20);
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001150 suggest_kernel_config("CONFIG_IRQBALANCE", 0,
Arjan van der Ven825ec692007-05-24 17:41:06 +00001151 _("Suggestion: Disable the CONFIG_IRQBALANCE kernel configuration option.\n" "The in-kernel irq balancer is obsolete and wakes the CPU up far more than needed."), 3);
Arjan van der Ven96abef12007-06-17 06:30:21 +00001152 suggest_kernel_config("CONFIG_CPU_FREQ_STAT", 1,
1153 _("Suggestion: Enable the CONFIG_CPU_FREQ_STAT kernel configuration option.\n"
Arjan van der Ven45b6a982007-06-17 16:38:59 +00001154 "This option allows PowerTOP to show P-state percentages \n" "P-states correspond to CPU frequencies."), 2);
Arjan van der Venf6a22472007-06-27 04:50:13 +00001155 suggest_kernel_config("CONFIG_INOTIFY", 1,
1156 _("Suggestion: Enable the CONFIG_INOTIFY kernel configuration option.\n"
1157 "This option allows programs to wait for changes in files and directories\n"
1158 "instead of having to poll for these changes"), 5);
Arjan van der Ven96abef12007-06-17 06:30:21 +00001159
Arjan van der Vend1d13382007-05-26 21:24:16 +00001160
1161 /* suggest to stop beagle if it shows up in the top 20 and wakes up more than 10 times in the measurement */
1162 suggest_process_death("beagled : schedule_timeout", "beagled", lines, min(linehead,20), 10.0,
1163 _("Suggestion: Disable or remove 'beagle' from your system. \n"
1164 "Beagle is the program that indexes for easy desktop search, however it's \n"
1165 "not very efficient and costs a significant amount of battery life."), 30);
Arjan van der Ven95521af2007-05-27 17:49:39 +00001166 suggest_process_death("beagled : futex_wait (hrtimer_wakeup)", "beagled", lines, min(linehead,20), 10.0,
1167 _("Suggestion: Disable or remove 'beagle' from your system. \n"
1168 "Beagle is the program that indexes for easy desktop search, however it's \n"
1169 "not very efficient and costs a significant amount of battery life."), 30);
Arjan van der Vend1d13382007-05-26 21:24:16 +00001170
Arjan van der Ven6396cbb2007-10-28 19:47:16 +00001171 /* suggest to stop gnome-power-manager *only* if it shows up in the top 10 and wakes up more than 10 times in the measurement */
1172 /* note to distribution makers: There is no need to patch this out! */
1173 /* If you ship a recent enough g-p-m, the warning will not be there, */
1174 /* and if you ship a really old one the warning is really justified. */
Arjan van der Ven915f7982007-05-26 22:18:24 +00001175 suggest_process_death("gnome-power-man : schedule_timeout (process_timeout)", "gnome-power-manager", lines, min(linehead,10), 10.0,
Arjan van der Vend1d13382007-05-26 21:24:16 +00001176 _("Suggestion: Disable or remove 'gnome-power-manager' from your system. \n"
Arjan van der Venf0963632007-08-18 21:28:02 +00001177 "Older versions of gnome-power-manager wake up far more often than \n"
1178 "needed costing you some power."), 5);
Arjan van der Vend1d13382007-05-26 21:24:16 +00001179
1180 /* suggest to stop pcscd if it shows up in the top 50 and wakes up at all*/
Arjan van der Venba3cc272007-06-01 06:53:08 +00001181 suggest_process_death("pcscd : ", "pcscd", lines, min(linehead,50), 1.0,
Arjan van der Vend1d13382007-05-26 21:24:16 +00001182 _("Suggestion: Disable or remove 'pcscd' from your system. \n"
1183 "pcscd tends to keep the USB subsystem out of power save mode\n"
1184 "and your processor out of deeper powersave states."), 30);
1185
1186
Arjan van der Vene0fec352007-05-27 02:38:53 +00001187 /* suggest to stop hal polilng if it shows up in the top 50 and wakes up too much*/
Arjan van der Ven7a533422007-06-01 07:01:24 +00001188 suggest_process_death("hald-addon-stor : ", "hald-addon-storage", lines, min(linehead,50), 2.0,
Arjan van der Venbd4a8f92007-06-08 19:53:53 +00001189 _( "Suggestion: Disable 'hal' from polling your cdrom with: \n"
Auke Kok869de2c2008-11-26 18:58:21 +00001190 "hal-disable-polling --device /dev/cdrom 'hal' is the component that auto-opens a\n"
Arjan van der Venaf7d5762007-06-08 22:02:02 +00001191 "window if you plug in a CD but disables SATA power saving from kicking in."), 30);
Arjan van der Vene0fec352007-05-27 02:38:53 +00001192
Arjan van der Ven2484be22007-06-03 21:31:22 +00001193 /* suggest to kill sealert; it wakes up 10 times/second on a default F7 install*/
1194 suggest_process_death("/usr/bin/sealer : schedule_timeout (process_timeout)", "-/usr/bin/sealert", lines, min(linehead,20), 20.0,
1195 _("Disable the SE-Alert software by removing the 'setroubleshoot-server' rpm\n"
1196 "SE-Alert alerts you about SELinux policy violations, but also\n"
1197 "has a bug that wakes it up 10 times per second."), 20);
1198
Arjan van der Vene0fec352007-05-27 02:38:53 +00001199
Arjan van der Ven7fe32322007-05-24 17:52:56 +00001200 suggest_bluetooth_off();
1201 suggest_nmi_watchdog();
1202 suggest_laptop_mode();
Arjan van der Venabd58ea2007-05-28 19:06:10 +00001203 if (maxsleep > 15.0)
1204 suggest_hpet();
Arjan van der Venaed51c72007-05-25 16:11:50 +00001205 suggest_ac97_powersave();
Arjan van der Venaec529b2007-05-25 19:57:57 +00001206 suggest_wireless_powersave();
Arjan van der Ven2889d762007-05-26 14:13:41 +00001207 suggest_ondemand_governor();
Arjan van der Venc16dfc72007-06-08 03:49:35 +00001208 suggest_noatime();
Arjan van der Venf1cc3b92007-06-08 04:56:33 +00001209 suggest_sata_alpm();
1210 suggest_powersched();
Arjan van der Ven4193b602007-06-12 22:33:45 +00001211 suggest_xrandr_TV_off();
Arjan van der Venc29ff6b2007-06-16 17:00:46 +00001212 suggest_WOL_off();
Arjan van der Ven35b51972007-06-19 01:52:00 +00001213 suggest_writeback_time();
Arjan van der Ven9823a122007-07-28 03:49:35 +00001214 suggest_usb_autosuspend();
Arjan van der Vene3c78822008-03-27 04:57:39 +00001215 usb_activity_hint();
Anurag Singhc5781f82011-10-10 10:44:44 -07001216#endif
Arjan van der Ven0bddd722007-08-31 17:24:07 +00001217 if (dump) {
Anurag Singhc5781f82011-10-10 10:44:44 -07001218#ifndef NO_SUGGESTIONS
Arjan van der Ven0bddd722007-08-31 17:24:07 +00001219 print_all_suggestions();
Arjan van der Vena332b9e2008-03-27 03:40:55 +00001220 display_usb_activity();
Anurag Singhc5781f82011-10-10 10:44:44 -07001221#endif
Arjan van der Ven0bddd722007-08-31 17:24:07 +00001222 exit(EXIT_SUCCESS);
1223 }
1224
Anurag Singhc5781f82011-10-10 10:44:44 -07001225#ifndef NO_SUGGESTIONS
Arjan van der Vend1d13382007-05-26 21:24:16 +00001226 if (!key)
1227 pick_suggestion();
Anurag Singhc5781f82011-10-10 10:44:44 -07001228#endif
1229#ifndef NO_NCURSES
Arjan van der Venb43056b2007-05-24 20:47:42 +00001230 show_title_bar();
Anurag Singhc5781f82011-10-10 10:44:44 -07001231#endif
Arjan van der Venef08f1c2007-05-16 18:28:57 +00001232 fflush(stdout);
Anurag Singhc5781f82011-10-10 10:44:44 -07001233
Arjan van der Vene74d6b72007-06-23 18:23:55 +00001234 if (!key && ticktime >= 4.8) { /* quiet down the effects of any IO to xterms */
1235 FD_ZERO(&rfds);
1236 FD_SET(0, &rfds);
1237 tv.tv_sec = 3;
1238 tv.tv_usec = 0;
1239 key = select(1, &rfds, NULL, NULL, &tv);
1240 }
Arjan van der Ven12e55ca2007-05-12 22:52:54 +00001241
Arjan van der Ven28615352007-05-11 20:08:05 +00001242 read_data(&cur_usage[0], &cur_duration[0]);
1243 memcpy(last_usage, cur_usage, sizeof(last_usage));
1244 memcpy(last_duration, cur_duration, sizeof(last_duration));
Arjan van der Ven41cc9942007-05-24 20:20:19 +00001245
Arjan van der Ven41cc9942007-05-24 20:20:19 +00001246
1247
Arjan van der Ven28615352007-05-11 20:08:05 +00001248 }
Arjan van der Vena332b9e2008-03-27 03:40:55 +00001249
Arjan van der Ven28615352007-05-11 20:08:05 +00001250 return 0;
1251}