• 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 <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 
check_latencytop()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,
47                                                int tid, int fatal);
48 
49 static struct latency_entry* alloc_latency_entry(void);
50 
51 static void set_latencytop(int on);
52 static struct latency_entry* read_latency_file(FILE* f, struct latency_entry* list);
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,
123                 "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         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])) 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 
read_thread_stats(struct latency_entry * list,int erase,int pid,int tid,int fatal)230 static struct latency_entry* read_thread_stats(struct latency_entry* list, int erase, int pid,
231                                                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 
find_latency_entry(struct latency_entry * head,char * reason)288 static struct latency_entry* find_latency_entry(struct latency_entry* head, char* reason) {
289     struct latency_entry* e;
290 
291     e = head;
292 
293     while (e) {
294         if (!strcmp(e->reason, reason)) return e;
295         e = e->next;
296     }
297 
298     return NULL;
299 }
300 
set_latencytop(int on)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 
read_latency_file(FILE * f,struct latency_entry * list)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) e->max = max;
341                 e->total += total;
342             } else {
343                 e = alloc_latency_entry();
344                 e->count = count;
345                 e->max = max;
346                 e->total = total;
347                 strcpy(e->reason, reason);
348                 e->next = head;
349                 head = e;
350             }
351         }
352     }
353 
354     return head;
355 }
356 
print_latency_entries(struct latency_entry * head)357 static void print_latency_entries(struct latency_entry* head) {
358     struct latency_entry *e, **array;
359     unsigned long average;
360     int i, count;
361 
362     e = head;
363     count = 0;
364     while (e) {
365         count++;
366         e = e->next;
367     }
368 
369     e = head;
370     array = calloc(count, sizeof(struct latency_entry*));
371     if (!array) {
372         fprintf(stderr, "Error allocating array: %s\n", strerror(errno));
373         exit(EXIT_FAILURE);
374     }
375     for (i = 0; i < count; i++) {
376         array[i] = e;
377         e = e->next;
378     }
379 
380     qsort(array, count, sizeof(struct latency_entry*), &lat_cmp);
381 
382     printf("%10s  %10s  %7s  %s\n", "Maximum", "Average", "Count", "Reason");
383     for (i = 0; i < count; i++) {
384         e = array[i];
385         average = e->total / e->count;
386         printf("%4lu.%02lu ms  %4lu.%02lu ms  %7ld  %s\n", e->max / 1000, (e->max % 1000) / 10,
387                average / 1000, (average % 1000) / 10, e->count, e->reason);
388     }
389 
390     free(array);
391 }
392 
signal_handler(int sig)393 static void signal_handler(int sig) {
394     exit(EXIT_SUCCESS);
395 }
396 
disable_latencytop(void)397 static void disable_latencytop(void) {
398     set_latencytop(0);
399 }
400 
clear_screen(void)401 static void clear_screen(void) {
402     printf("\n\n");
403 }
404 
usage(const char * cmd)405 static void usage(const char* cmd) {
406     fprintf(stderr,
407             "Usage: %s [ -d delay ] [ -n iterations ] [ -p pid [ -t tid ] ] [ -h ]\n"
408             "    -d delay       Time to sleep between updates.\n"
409             "    -n iterations  Number of updates to show (0 = infinite).\n"
410             "    -p pid         Process to monitor (default is all).\n"
411             "    -t tid         Thread (within specified process) to monitor (default is all).\n"
412             "    -h             Display this help screen.\n",
413             cmd);
414 }
415 
numcmp(const long long a,const long long b)416 static int numcmp(const long long a, const long long b) {
417     if (a < b) return -1;
418     if (a > b) return 1;
419     return 0;
420 }
421 
lat_cmp(const void * a,const void * b)422 static int lat_cmp(const void* a, const void* b) {
423     const struct latency_entry *pa, *pb;
424 
425     pa = (*((struct latency_entry**)a));
426     pb = (*((struct latency_entry**)b));
427 
428     return numcmp(pb->max, pa->max);
429 }
430