OSDN Git Service

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