• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
check_latencytop()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 
main(int argc,char * argv[])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 
read_global_stats(struct latency_entry * list,int erase)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 
read_process_stats(struct latency_entry * list,int erase,int pid)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 
read_thread_stats(struct latency_entry * list,int erase,int pid,int tid,int fatal)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 
alloc_latency_entry(void)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 
free_latency_entry(struct latency_entry * e)288 static void free_latency_entry(struct latency_entry *e) {
289     e->next = free_entries;
290     free_entries = e;
291 }
292 
find_latency_entry(struct latency_entry * head,char * reason)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 
set_latencytop(int on)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 
erase_latency_file(FILE * f)321 static void erase_latency_file(FILE *f) {
322     fprintf(f, "erase\n");
323 }
324 
read_latency_file(FILE * f,struct latency_entry * list)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 
print_latency_entries(struct latency_entry * head)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 
signal_handler(int sig)407 static void signal_handler(int sig) {
408     exit(EXIT_SUCCESS);
409 }
410 
disable_latencytop(void)411 static void disable_latencytop(void) {
412     set_latencytop(0);
413 }
414 
clear_screen(void)415 static void clear_screen(void) {
416     printf("\n\n");
417 }
418 
usage(const char * cmd)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 
numcmp(const long long a,const long long b)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 
lat_cmp(const void * a,const void * b)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 }
443