• 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 <dirent.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <limits.h>
21 #include <poll.h>
22 #include <signal.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/inotify.h>
28 #include <sys/stat.h>
29 #include <sys/time.h>
30 #include <sys/wait.h>
31 #include <time.h>
32 #include <unistd.h>
33 
34 #include <cutils/properties.h>
35 #include <cutils/sockets.h>
36 #include <private/android_filesystem_config.h>
37 
38 #include "dumpstate.h"
39 
for_each_pid(void (* func)(int,const char *),const char * header)40 void for_each_pid(void (*func)(int, const char *), const char *header) {
41     DIR *d;
42     struct dirent *de;
43 
44     if (!(d = opendir("/proc"))) {
45         printf("Failed to open /proc (%s)\n", strerror(errno));
46         return;
47     }
48 
49     printf("\n------ %s ------\n", header);
50     while ((de = readdir(d))) {
51         int pid;
52         int fd;
53         char cmdpath[255];
54         char cmdline[255];
55 
56         if (!(pid = atoi(de->d_name))) {
57             continue;
58         }
59 
60         sprintf(cmdpath,"/proc/%d/cmdline", pid);
61         memset(cmdline, 0, sizeof(cmdline));
62         if ((fd = open(cmdpath, O_RDONLY)) < 0) {
63             strcpy(cmdline, "N/A");
64         } else {
65             read(fd, cmdline, sizeof(cmdline));
66             close(fd);
67         }
68         func(pid, cmdline);
69     }
70 
71     closedir(d);
72 }
73 
show_wchan(int pid,const char * name)74 void show_wchan(int pid, const char *name) {
75     char path[255];
76     char buffer[255];
77     int fd;
78 
79     memset(buffer, 0, sizeof(buffer));
80 
81     sprintf(path, "/proc/%d/wchan", pid);
82     if ((fd = open(path, O_RDONLY)) < 0) {
83         printf("Failed to open '%s' (%s)\n", path, strerror(errno));
84         return;
85     }
86 
87     if (read(fd, buffer, sizeof(buffer)) < 0) {
88         printf("Failed to read '%s' (%s)\n", path, strerror(errno));
89         goto out_close;
90     }
91 
92     printf("%-7d %-32s %s\n", pid, name, buffer);
93 
94 out_close:
95     close(fd);
96     return;
97 }
98 
99 /* prints the contents of a file */
dump_file(const char * title,const char * path)100 int dump_file(const char *title, const char* path) {
101     char buffer[32768];
102     int fd = open(path, O_RDONLY);
103     if (fd < 0) {
104         int err = errno;
105         if (title) printf("------ %s (%s) ------\n", title, path);
106         printf("*** %s: %s\n", path, strerror(err));
107         if (title) printf("\n");
108         return -1;
109     }
110 
111     if (title) printf("------ %s (%s", title, path);
112 
113     if (title) {
114         struct stat st;
115         if (memcmp(path, "/proc/", 6) && memcmp(path, "/sys/", 5) && !fstat(fd, &st)) {
116             char stamp[80];
117             time_t mtime = st.st_mtime;
118             strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
119             printf(": %s", stamp);
120         }
121         printf(") ------\n");
122     }
123 
124     int newline = 0;
125     for (;;) {
126         int ret = read(fd, buffer, sizeof(buffer));
127         if (ret > 0) {
128             newline = (buffer[ret - 1] == '\n');
129             ret = fwrite(buffer, ret, 1, stdout);
130         }
131         if (ret <= 0) break;
132     }
133 
134     close(fd);
135     if (!newline) printf("\n");
136     if (title) printf("\n");
137     return 0;
138 }
139 
140 /* forks a command and waits for it to finish */
run_command(const char * title,int timeout_seconds,const char * command,...)141 int run_command(const char *title, int timeout_seconds, const char *command, ...) {
142     fflush(stdout);
143     clock_t start = clock();
144     pid_t pid = fork();
145 
146     /* handle error case */
147     if (pid < 0) {
148         printf("*** fork: %s\n", strerror(errno));
149         return pid;
150     }
151 
152     /* handle child case */
153     if (pid == 0) {
154         const char *args[1024] = {command};
155         size_t arg;
156 
157         va_list ap;
158         va_start(ap, command);
159         if (title) printf("------ %s (%s", title, command);
160         for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
161             args[arg] = va_arg(ap, const char *);
162             if (args[arg] == NULL) break;
163             if (title) printf(" %s", args[arg]);
164         }
165         if (title) printf(") ------\n");
166         fflush(stdout);
167 
168         execvp(command, (char**) args);
169         printf("*** exec(%s): %s\n", command, strerror(errno));
170         _exit(-1);
171     }
172 
173     /* handle parent case */
174     for (;;) {
175         int status;
176         pid_t p = waitpid(pid, &status, WNOHANG);
177         float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC;
178         if (p == pid) {
179             if (WIFSIGNALED(status)) {
180                 printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
181             } else if (WEXITSTATUS(status) > 0) {
182                 printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
183             }
184             if (title) printf("[%s: %.1fs elapsed]\n\n", command, elapsed);
185             return status;
186         }
187 
188         if (timeout_seconds && elapsed > timeout_seconds) {
189             printf("*** %s: Timed out after %.1fs (killing pid %d)\n", command, elapsed, pid);
190             kill(pid, SIGTERM);
191             return -1;
192         }
193 
194         usleep(100000);  // poll every 0.1 sec
195     }
196 }
197 
198 size_t num_props = 0;
199 static char* props[2000];
200 
print_prop(const char * key,const char * name,void * user)201 static void print_prop(const char *key, const char *name, void *user) {
202     (void) user;
203     if (num_props < sizeof(props) / sizeof(props[0])) {
204         char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
205         snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
206         props[num_props++] = strdup(buf);
207     }
208 }
209 
compare_prop(const void * a,const void * b)210 static int compare_prop(const void *a, const void *b) {
211     return strcmp(*(char * const *) a, *(char * const *) b);
212 }
213 
214 /* prints all the system properties */
print_properties()215 void print_properties() {
216     size_t i;
217     num_props = 0;
218     property_list(print_prop, NULL);
219     qsort(&props, num_props, sizeof(props[0]), compare_prop);
220 
221     printf("------ SYSTEM PROPERTIES ------\n");
222     for (i = 0; i < num_props; ++i) {
223         fputs(props[i], stdout);
224         free(props[i]);
225     }
226     printf("\n");
227 }
228 
229 /* redirect output to a service control socket */
redirect_to_socket(FILE * redirect,const char * service)230 void redirect_to_socket(FILE *redirect, const char *service) {
231     int s = android_get_control_socket(service);
232     if (s < 0) {
233         fprintf(stderr, "android_get_control_socket(%s): %s\n", service, strerror(errno));
234         exit(1);
235     }
236     if (listen(s, 4) < 0) {
237         fprintf(stderr, "listen(control socket): %s\n", strerror(errno));
238         exit(1);
239     }
240 
241     struct sockaddr addr;
242     socklen_t alen = sizeof(addr);
243     int fd = accept(s, &addr, &alen);
244     if (fd < 0) {
245         fprintf(stderr, "accept(control socket): %s\n", strerror(errno));
246         exit(1);
247     }
248 
249     fflush(redirect);
250     dup2(fd, fileno(redirect));
251     close(fd);
252 }
253 
254 /* redirect output to a file, optionally gzipping; returns gzip pid (or -1) */
redirect_to_file(FILE * redirect,char * path,int gzip_level)255 pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level) {
256     char *chp = path;
257 
258     /* skip initial slash */
259     if (chp[0] == '/')
260         chp++;
261 
262     /* create leading directories, if necessary */
263     while (chp && chp[0]) {
264         chp = strchr(chp, '/');
265         if (chp) {
266             *chp = 0;
267             mkdir(path, 0775);  /* drwxrwxr-x */
268             *chp++ = '/';
269         }
270     }
271 
272     int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
273     if (fd < 0) {
274         fprintf(stderr, "%s: %s\n", path, strerror(errno));
275         exit(1);
276     }
277 
278     pid_t gzip_pid = -1;
279     if (gzip_level > 0) {
280         int fds[2];
281         if (pipe(fds)) {
282             fprintf(stderr, "pipe: %s\n", strerror(errno));
283             exit(1);
284         }
285 
286         fflush(redirect);
287         fflush(stdout);
288 
289         gzip_pid = fork();
290         if (gzip_pid < 0) {
291             fprintf(stderr, "fork: %s\n", strerror(errno));
292             exit(1);
293         }
294 
295         if (gzip_pid == 0) {
296             dup2(fds[0], STDIN_FILENO);
297             dup2(fd, STDOUT_FILENO);
298 
299             close(fd);
300             close(fds[0]);
301             close(fds[1]);
302 
303             char level[10];
304             snprintf(level, sizeof(level), "-%d", gzip_level);
305             execlp("gzip", "gzip", level, NULL);
306             fprintf(stderr, "exec(gzip): %s\n", strerror(errno));
307             _exit(-1);
308         }
309 
310         close(fd);
311         close(fds[0]);
312         fd = fds[1];
313     }
314 
315     dup2(fd, fileno(redirect));
316     close(fd);
317     return gzip_pid;
318 }
319 
320 /* dump Dalvik stack traces, return the trace file location (NULL if none) */
dump_vm_traces()321 const char *dump_vm_traces() {
322     char traces_path[PROPERTY_VALUE_MAX] = "";
323     property_get("dalvik.vm.stack-trace-file", traces_path, "");
324     if (!traces_path[0]) return NULL;
325 
326     /* move the old traces.txt (if any) out of the way temporarily */
327     char anr_traces_path[PATH_MAX];
328     strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
329     strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
330     if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
331         fprintf(stderr, "rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
332         return NULL;  // Can't rename old traces.txt -- no permission? -- leave it alone instead
333     }
334 
335     /* make the directory if necessary */
336     char anr_traces_dir[PATH_MAX];
337     strlcpy(anr_traces_dir, traces_path, sizeof(anr_traces_dir));
338     char *slash = strrchr(anr_traces_dir, '/');
339     if (slash != NULL) {
340         *slash = '\0';
341         if (!mkdir(anr_traces_dir, 0775)) {
342             chown(anr_traces_dir, AID_SYSTEM, AID_SYSTEM);
343         } else if (errno != EEXIST) {
344             fprintf(stderr, "mkdir(%s): %s\n", anr_traces_dir, strerror(errno));
345             return NULL;
346         }
347     }
348 
349     /* create a new, empty traces.txt file to receive stack dumps */
350     int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC, 0666);  /* -rw-rw-rw- */
351     if (fd < 0) {
352         fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
353         return NULL;
354     }
355     close(fd);
356 
357     /* walk /proc and kill -QUIT all Dalvik processes */
358     DIR *proc = opendir("/proc");
359     if (proc == NULL) {
360         fprintf(stderr, "/proc: %s\n", strerror(errno));
361         return NULL;
362     }
363 
364     /* use inotify to find when processes are done dumping */
365     int ifd = inotify_init();
366     if (ifd < 0) {
367         fprintf(stderr, "inotify_init: %s\n", strerror(errno));
368         return NULL;
369     }
370 
371     int wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
372     if (wfd < 0) {
373         fprintf(stderr, "inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
374         return NULL;
375     }
376 
377     struct dirent *d;
378     int dalvik_found = 0;
379     while ((d = readdir(proc))) {
380         int pid = atoi(d->d_name);
381         if (pid <= 0) continue;
382 
383         /* identify Dalvik: /proc/(pid)/exe = /system/bin/app_process */
384         char path[PATH_MAX], data[PATH_MAX];
385         snprintf(path, sizeof(path), "/proc/%d/exe", pid);
386         size_t len = readlink(path, data, sizeof(data) - 1);
387         if (len <= 0 || memcmp(data, "/system/bin/app_process", 23)) continue;
388 
389         /* skip zygote -- it won't dump its stack anyway */
390         snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
391         int fd = open(path, O_RDONLY);
392         len = read(fd, data, sizeof(data) - 1);
393         close(fd);
394         if (len <= 0 || !memcmp(data, "zygote", 6)) continue;
395 
396         ++dalvik_found;
397         if (kill(pid, SIGQUIT)) {
398             fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
399             continue;
400         }
401 
402         /* wait for the writable-close notification from inotify */
403         struct pollfd pfd = { ifd, POLLIN, 0 };
404         int ret = poll(&pfd, 1, 200);  /* 200 msec timeout */
405         if (ret < 0) {
406             fprintf(stderr, "poll: %s\n", strerror(errno));
407         } else if (ret == 0) {
408             fprintf(stderr, "warning: timed out dumping pid %d\n", pid);
409         } else {
410             struct inotify_event ie;
411             read(ifd, &ie, sizeof(ie));
412         }
413     }
414 
415     close(ifd);
416     if (dalvik_found == 0) {
417         fprintf(stderr, "Warning: no Dalvik processes found to dump stacks\n");
418     }
419 
420     static char dump_traces_path[PATH_MAX];
421     strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
422     strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
423     if (rename(traces_path, dump_traces_path)) {
424         fprintf(stderr, "rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
425         return NULL;
426     }
427 
428     /* replace the saved [ANR] traces.txt file */
429     rename(anr_traces_path, traces_path);
430     return dump_traces_path;
431 }
432