OSDN Git Service

Fix binderAddInts benchmark
[android-x86/system-extras.git] / latencytop / latencytop.c
1 /*
2  * Copyright (C) 2008 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 #include <ctype.h>
18 #include <dirent.h>
19 #include <errno.h>
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #define MAX_LINE 512
27 #define MAX_FILENAME 64
28
29 const char *EXPECTED_VERSION = "Latency Top version : v0.1\n";
30 const char *SYSCTL_FILE = "/proc/sys/kernel/latencytop";
31 const char *GLOBAL_STATS_FILE = "/proc/latency_stats";
32 const char *THREAD_STATS_FILE_FORMAT = "/proc/%d/task/%d/latency";
33
34 struct latency_entry {
35     struct latency_entry *next;
36     unsigned long count;
37     unsigned long max;
38     unsigned long total;
39     char reason[MAX_LINE];
40 };
41
42 static inline void check_latencytop() { }
43
44 static struct latency_entry *read_global_stats(struct latency_entry *list, int erase);
45 static struct latency_entry *read_process_stats(struct latency_entry *list, int erase, int pid);
46 static struct latency_entry *read_thread_stats(struct latency_entry *list, int erase, int pid, int tid, int fatal);
47
48 static struct latency_entry *alloc_latency_entry(void);
49 static void free_latency_entry(struct latency_entry *e);
50
51 static void set_latencytop(int on);
52 static struct latency_entry *read_latency_file(FILE *f, struct latency_entry *list);
53 static void erase_latency_file(FILE *f);
54
55 static struct latency_entry *find_latency_entry(struct latency_entry *e, char *reason);
56 static void print_latency_entries(struct latency_entry *head);
57
58 static void signal_handler(int sig);
59 static void disable_latencytop(void);
60
61 static int numcmp(const long long a, const long long b);
62 static int lat_cmp(const void *a, const void *b);
63
64 static void clear_screen(void);
65 static void usage(const char *cmd);
66
67 struct latency_entry *free_entries;
68
69 int main(int argc, char *argv[]) {
70     struct latency_entry *e;
71     int delay, iterations;
72     int pid, tid;
73     int count, erase;
74     int i;
75
76     delay = 1;
77     iterations = 0;
78     pid = tid = 0;
79     
80     for (i = 1; i < argc; i++) {
81         if (!strcmp(argv[i], "-d")) {
82             if (i >= argc - 1) {
83                 fprintf(stderr, "Option -d expects an argument.\n");
84                 exit(EXIT_FAILURE);
85             }
86             delay = atoi(argv[++i]);
87             continue;
88         }
89         if (!strcmp(argv[i], "-n")) {
90             if (i >= argc - 1) {
91                 fprintf(stderr, "Option -n expects an argument.\n");
92                 exit(EXIT_FAILURE);
93             }
94             iterations = atoi(argv[++i]);
95             continue;
96         }
97         if (!strcmp(argv[i], "-h")) {
98             usage(argv[0]);
99             exit(EXIT_SUCCESS);
100         }
101         if (!strcmp(argv[i], "-p")) {
102             if (i >= argc - 1) {
103                 fprintf(stderr, "Option -p expects an argument.\n");
104                 exit(EXIT_FAILURE);
105             }
106             pid = atoi(argv[++i]);
107             continue;
108         }
109         if (!strcmp(argv[i], "-t")) {
110             if (i >= argc - 1) {
111                 fprintf(stderr, "Option -t expects an argument.\n");
112                 exit(EXIT_FAILURE);
113             }
114             tid = atoi(argv[++i]);
115             continue;
116         }
117         fprintf(stderr, "Invalid argument \"%s\".\n", argv[i]);
118         usage(argv[0]);
119         exit(EXIT_FAILURE);
120     }
121
122     if (tid && !pid) {
123         fprintf(stderr, "If you provide a thread ID with -t, you must provide a process ID with -p.\n");
124         exit(EXIT_FAILURE);
125     }
126
127     check_latencytop();
128
129     free_entries = NULL;
130
131     signal(SIGINT, &signal_handler);
132     signal(SIGTERM, &signal_handler);
133
134     atexit(&disable_latencytop);
135
136     set_latencytop(1);
137
138     count = 0;
139     erase = 1;
140
141     while ((iterations == 0) || (count++ < iterations)) {
142
143         sleep(delay);
144
145         e = NULL;
146         if (pid) {
147             if (tid) {
148                 e = read_thread_stats(e, erase, pid, tid, 1);
149             } else {
150                 e = read_process_stats(e, erase, pid);
151             }
152         } else {
153             e = read_global_stats(e, erase);
154         }
155         erase = 0;
156
157         clear_screen();
158         if (pid) {
159             if (tid) {
160                 printf("Latencies for thread %d in process %d:\n", tid, pid);
161             } else {
162                 printf("Latencies for process %d:\n", pid);
163             }
164         } else {
165             printf("Latencies across all processes:\n");
166         }
167         print_latency_entries(e);
168     }
169
170     set_latencytop(0);
171
172     return 0;
173 }
174
175 static struct latency_entry *read_global_stats(struct latency_entry *list, int erase) {
176     FILE *f;
177     struct latency_entry *e;
178
179     if (erase) {
180         f = fopen(GLOBAL_STATS_FILE, "w");
181         if (!f) {
182             fprintf(stderr, "Could not open global latency stats file: %s\n", strerror(errno));
183             exit(EXIT_FAILURE);
184         }
185         fprintf(f, "erase\n");
186         fclose(f);
187     }
188     
189     f = fopen(GLOBAL_STATS_FILE, "r");
190     if (!f) {
191         fprintf(stderr, "Could not open global latency stats file: %s\n", strerror(errno));
192         exit(EXIT_FAILURE);
193     }
194
195     e = read_latency_file(f, list);
196
197     fclose(f);
198
199     return e;
200 }
201
202 static struct latency_entry *read_process_stats(struct latency_entry *list, int erase, int pid) {
203     char dirname[MAX_FILENAME];
204     DIR *dir;
205     struct dirent *ent;
206     struct latency_entry *e;
207     int tid;
208
209     sprintf(dirname, "/proc/%d/task", pid);
210     dir = opendir(dirname);
211     if (!dir) {
212         fprintf(stderr, "Could not open task dir for process %d.\n", pid);
213         fprintf(stderr, "Perhaps the process has terminated?\n");
214         exit(EXIT_FAILURE);
215     }
216
217     e = list;
218     while ((ent = readdir(dir))) {
219         if (!isdigit(ent->d_name[0]))
220             continue;
221
222         tid = atoi(ent->d_name);
223
224         e = read_thread_stats(e, erase, pid, tid, 0);
225     }
226
227     closedir(dir);
228
229     return e;
230 }
231
232 static struct latency_entry *read_thread_stats(struct latency_entry *list, int erase, int pid, int tid, int fatal) {
233     char filename[MAX_FILENAME];
234     FILE *f;
235     struct latency_entry *e;
236
237     sprintf(filename, THREAD_STATS_FILE_FORMAT, pid, tid);
238
239     if (erase) {
240         f = fopen(filename, "w");
241         if (!f) {
242             if (fatal) {
243                 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
244                 fprintf(stderr, "Perhaps the process or thread has terminated?\n");
245                 exit(EXIT_FAILURE);
246             } else {
247                 return list;
248             }
249         }
250         fprintf(f, "erase\n");
251         fclose(f);
252     }
253     
254     f = fopen(GLOBAL_STATS_FILE, "r");
255     if (!f) {
256         if (fatal) {
257             fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
258             fprintf(stderr, "Perhaps the process or thread has terminated?\n");
259             exit(EXIT_FAILURE);
260         } else {
261             return list;
262         }
263     }
264
265     e = read_latency_file(f, list);
266
267     fclose(f);
268
269     return e;
270 }
271
272 static struct latency_entry *alloc_latency_entry(void) {
273     struct latency_entry *e;
274
275     if (free_entries) {
276         e = free_entries;
277         free_entries = free_entries->next;
278     } else {
279         e = calloc(1, sizeof(struct latency_entry));
280         if (!e) {
281             fprintf(stderr, "Could not allocate latency entry: %s\n", strerror(errno));
282             exit(EXIT_FAILURE);
283         }
284     }
285
286     return e;
287 }
288
289 static void free_latency_entry(struct latency_entry *e) {
290     e->next = free_entries;
291     free_entries = e;
292 }
293
294 static struct latency_entry *find_latency_entry(struct latency_entry *head, char *reason) {
295     struct latency_entry *e;
296
297     e = head;
298
299     while (e) {
300         if (!strcmp(e->reason, reason))
301             return e;
302         e = e->next;
303     }
304
305     return NULL;
306 }
307
308 static void set_latencytop(int on) {
309     FILE *f;
310
311     f = fopen(SYSCTL_FILE, "w");
312     if (!f) {
313         fprintf(stderr, "Could not open %s: %s\n", SYSCTL_FILE, strerror(errno));
314         exit(EXIT_FAILURE);
315     }
316
317     fprintf(f, "%d\n", on);
318
319     fclose(f);
320 }
321
322 static void erase_latency_file(FILE *f) {
323     fprintf(f, "erase\n");
324 }
325
326 static struct latency_entry *read_latency_file(FILE *f, struct latency_entry *list) {
327     struct latency_entry *e, *head;
328     char line[MAX_LINE];
329     unsigned long count, max, total;
330     char reason[MAX_LINE];
331
332     head = list;
333
334     if (!fgets(line, MAX_LINE, f)) {
335         fprintf(stderr, "Could not read latency file version: %s\n", strerror(errno));
336         exit(EXIT_FAILURE);
337     }
338
339     if (strcmp(line, EXPECTED_VERSION) != 0) {
340         fprintf(stderr, "Expected version: %s\n", EXPECTED_VERSION);
341         fprintf(stderr, "But got version: %s", line);
342         exit(EXIT_FAILURE);
343     }
344
345     while (fgets(line, MAX_LINE, f)) {
346         sscanf(line, "%ld %ld %ld %s", &count, &total, &max, reason);
347         if (max > 0 || total > 0) {
348             e = find_latency_entry(head, reason);
349             if (e) {
350                 e->count += count;
351                 if (max > e->max)
352                     e->max = max;
353                 e->total += total;
354             } else {
355                 e = alloc_latency_entry();
356                 e->count = count;
357                 e->max = max;
358                 e->total = total;
359                 strcpy(e->reason, reason);
360                 e->next = head;
361                 head = e;
362             }
363         }
364     }
365
366     return head;
367 }
368
369 static void print_latency_entries(struct latency_entry *head) {
370     struct latency_entry *e, **array;
371     unsigned long average;
372     int i, count;
373
374     e = head;
375     count = 0;
376     while (e) {
377         count++;
378         e = e->next;
379     }
380
381     e = head;
382     array = calloc(count, sizeof(struct latency_entry *));
383     if (!array) {
384         fprintf(stderr, "Error allocating array: %s\n", strerror(errno));
385         exit(EXIT_FAILURE);
386     }
387     for (i = 0; i < count; i++) {
388         array[i] = e;
389         e = e->next;
390     }
391
392     qsort(array, count, sizeof(struct latency_entry *), &lat_cmp);
393
394     printf("%10s  %10s  %7s  %s\n", "Maximum", "Average", "Count", "Reason");
395     for (i = 0; i < count; i++) {
396         e = array[i];
397         average = e->total / e->count;
398         printf("%4lu.%02lu ms  %4lu.%02lu ms  %7ld  %s\n",
399             e->max / 1000, (e->max % 1000) / 10,
400             average / 1000, (average % 1000) / 10,
401             e->count,
402             e->reason);
403     }
404
405     free(array);
406 }
407
408 static void signal_handler(int sig) {
409     exit(EXIT_SUCCESS);
410 }
411
412 static void disable_latencytop(void) {
413     set_latencytop(0);
414 }
415
416 static void clear_screen(void) {
417     printf("\n\n");
418 }
419
420 static void usage(const char *cmd) {
421     fprintf(stderr, "Usage: %s [ -d delay ] [ -n iterations ] [ -p pid [ -t tid ] ] [ -h ]\n"
422                     "    -d delay       Time to sleep between updates.\n"
423                     "    -n iterations  Number of updates to show (0 = infinite).\n"
424                     "    -p pid         Process to monitor (default is all).\n"
425                     "    -t tid         Thread (within specified process) to monitor (default is all).\n"
426                     "    -h             Display this help screen.\n",
427         cmd);
428 }
429
430 static int numcmp(const long long a, const long long b) {
431     if (a < b) return -1;
432     if (a > b) return 1;
433     return 0;
434 }
435
436 static int lat_cmp(const void *a, const void *b) {
437     const struct latency_entry *pa, *pb;
438
439     pa = (*((struct latency_entry **)a));
440     pb = (*((struct latency_entry **)b));
441
442     return numcmp(pb->max, pa->max);
443 }