OSDN Git Service

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