utils.c revision 957983787d7a753d34b582ae0165160f4ebf815c
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 <sys/klog.h>
32#include <time.h>
33#include <unistd.h>
34#include <sys/prctl.h>
35
36#include <cutils/debugger.h>
37#include <cutils/properties.h>
38#include <cutils/sockets.h>
39#include <private/android_filesystem_config.h>
40
41#include <selinux/android.h>
42
43#include "dumpstate.h"
44
45/* list of native processes to include in the native dumps */
46static const char* native_processes_to_dump[] = {
47        "/system/bin/drmserver",
48        "/system/bin/mediaserver",
49        "/system/bin/sdcard",
50        "/system/bin/surfaceflinger",
51        NULL,
52};
53
54void for_each_pid(void (*func)(int, const char *), const char *header) {
55    DIR *d;
56    struct dirent *de;
57
58    if (!(d = opendir("/proc"))) {
59        printf("Failed to open /proc (%s)\n", strerror(errno));
60        return;
61    }
62
63    printf("\n------ %s ------\n", header);
64    while ((de = readdir(d))) {
65        int pid;
66        int fd;
67        char cmdpath[255];
68        char cmdline[255];
69
70        if (!(pid = atoi(de->d_name))) {
71            continue;
72        }
73
74        sprintf(cmdpath,"/proc/%d/cmdline", pid);
75        memset(cmdline, 0, sizeof(cmdline));
76        if ((fd = open(cmdpath, O_RDONLY)) < 0) {
77            strcpy(cmdline, "N/A");
78        } else {
79            read(fd, cmdline, sizeof(cmdline));
80            close(fd);
81        }
82        func(pid, cmdline);
83    }
84
85    closedir(d);
86}
87
88void show_wchan(int pid, const char *name) {
89    char path[255];
90    char buffer[255];
91    int fd;
92
93    memset(buffer, 0, sizeof(buffer));
94
95    sprintf(path, "/proc/%d/wchan", pid);
96    if ((fd = open(path, O_RDONLY)) < 0) {
97        printf("Failed to open '%s' (%s)\n", path, strerror(errno));
98        return;
99    }
100
101    if (read(fd, buffer, sizeof(buffer)) < 0) {
102        printf("Failed to read '%s' (%s)\n", path, strerror(errno));
103        goto out_close;
104    }
105
106    printf("%-7d %-32s %s\n", pid, name, buffer);
107
108out_close:
109    close(fd);
110    return;
111}
112
113void do_dmesg() {
114    printf("------ KERNEL LOG (dmesg) ------\n");
115    /* Get size of kernel buffer */
116    int size = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
117    if (size <= 0) {
118        printf("Unexpected klogctl return value: %d\n\n", size);
119        return;
120    }
121    char *buf = (char *) malloc(size + 1);
122    if (buf == NULL) {
123        printf("memory allocation failed\n\n");
124        return;
125    }
126    int retval = klogctl(KLOG_READ_ALL, buf, size);
127    if (retval < 0) {
128        printf("klogctl failure\n\n");
129        free(buf);
130        return;
131    }
132    buf[retval] = '\0';
133    printf("%s\n\n", buf);
134    free(buf);
135    return;
136}
137
138void do_showmap(int pid, const char *name) {
139    char title[255];
140    char arg[255];
141
142    sprintf(title, "SHOW MAP %d (%s)", pid, name);
143    sprintf(arg, "%d", pid);
144    run_command(title, 10, SU_PATH, "root", "showmap", arg, NULL);
145}
146
147/* prints the contents of a file */
148int dump_file(const char *title, const char* path) {
149    char buffer[32768];
150    int fd = open(path, O_RDONLY);
151    if (fd < 0) {
152        int err = errno;
153        if (title) printf("------ %s (%s) ------\n", title, path);
154        printf("*** %s: %s\n", path, strerror(err));
155        if (title) printf("\n");
156        return -1;
157    }
158
159    if (title) printf("------ %s (%s", title, path);
160
161    if (title) {
162        struct stat st;
163        if (memcmp(path, "/proc/", 6) && memcmp(path, "/sys/", 5) && !fstat(fd, &st)) {
164            char stamp[80];
165            time_t mtime = st.st_mtime;
166            strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
167            printf(": %s", stamp);
168        }
169        printf(") ------\n");
170    }
171
172    int newline = 0;
173    for (;;) {
174        int ret = read(fd, buffer, sizeof(buffer));
175        if (ret > 0) {
176            newline = (buffer[ret - 1] == '\n');
177            ret = fwrite(buffer, ret, 1, stdout);
178        }
179        if (ret <= 0) break;
180    }
181
182    close(fd);
183    if (!newline) printf("\n");
184    if (title) printf("\n");
185    return 0;
186}
187
188/* forks a command and waits for it to finish */
189int run_command(const char *title, int timeout_seconds, const char *command, ...) {
190    fflush(stdout);
191    clock_t start = clock();
192    pid_t pid = fork();
193
194    /* handle error case */
195    if (pid < 0) {
196        printf("*** fork: %s\n", strerror(errno));
197        return pid;
198    }
199
200    /* handle child case */
201    if (pid == 0) {
202        const char *args[1024] = {command};
203        size_t arg;
204
205        /* make sure the child dies when dumpstate dies */
206        prctl(PR_SET_PDEATHSIG, SIGKILL);
207
208        va_list ap;
209        va_start(ap, command);
210        if (title) printf("------ %s (%s", title, command);
211        for (arg = 1; arg < sizeof(args) / sizeof(args[0]); ++arg) {
212            args[arg] = va_arg(ap, const char *);
213            if (args[arg] == NULL) break;
214            if (title) printf(" %s", args[arg]);
215        }
216        if (title) printf(") ------\n");
217        fflush(stdout);
218
219        execvp(command, (char**) args);
220        printf("*** exec(%s): %s\n", command, strerror(errno));
221        fflush(stdout);
222        _exit(-1);
223    }
224
225    /* handle parent case */
226    for (;;) {
227        int status;
228        pid_t p = waitpid(pid, &status, WNOHANG);
229        float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC;
230        if (p == pid) {
231            if (WIFSIGNALED(status)) {
232                printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
233            } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
234                printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
235            }
236            if (title) printf("[%s: %.1fs elapsed]\n\n", command, elapsed);
237            return status;
238        }
239
240        if (timeout_seconds && elapsed > timeout_seconds) {
241            printf("*** %s: Timed out after %.1fs (killing pid %d)\n", command, elapsed, pid);
242            kill(pid, SIGTERM);
243            return -1;
244        }
245
246        usleep(100000);  // poll every 0.1 sec
247    }
248}
249
250size_t num_props = 0;
251static char* props[2000];
252
253static void print_prop(const char *key, const char *name, void *user) {
254    (void) user;
255    if (num_props < sizeof(props) / sizeof(props[0])) {
256        char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
257        snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
258        props[num_props++] = strdup(buf);
259    }
260}
261
262static int compare_prop(const void *a, const void *b) {
263    return strcmp(*(char * const *) a, *(char * const *) b);
264}
265
266/* prints all the system properties */
267void print_properties() {
268    size_t i;
269    num_props = 0;
270    property_list(print_prop, NULL);
271    qsort(&props, num_props, sizeof(props[0]), compare_prop);
272
273    printf("------ SYSTEM PROPERTIES ------\n");
274    for (i = 0; i < num_props; ++i) {
275        fputs(props[i], stdout);
276        free(props[i]);
277    }
278    printf("\n");
279}
280
281/* redirect output to a service control socket */
282void redirect_to_socket(FILE *redirect, const char *service) {
283    int s = android_get_control_socket(service);
284    if (s < 0) {
285        fprintf(stderr, "android_get_control_socket(%s): %s\n", service, strerror(errno));
286        exit(1);
287    }
288    if (listen(s, 4) < 0) {
289        fprintf(stderr, "listen(control socket): %s\n", strerror(errno));
290        exit(1);
291    }
292
293    struct sockaddr addr;
294    socklen_t alen = sizeof(addr);
295    int fd = accept(s, &addr, &alen);
296    if (fd < 0) {
297        fprintf(stderr, "accept(control socket): %s\n", strerror(errno));
298        exit(1);
299    }
300
301    fflush(redirect);
302    dup2(fd, fileno(redirect));
303    close(fd);
304}
305
306/* redirect output to a file, optionally gzipping; returns gzip pid (or -1) */
307pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level) {
308    char *chp = path;
309
310    /* skip initial slash */
311    if (chp[0] == '/')
312        chp++;
313
314    /* create leading directories, if necessary */
315    while (chp && chp[0]) {
316        chp = strchr(chp, '/');
317        if (chp) {
318            *chp = 0;
319            mkdir(path, 0775);  /* drwxrwxr-x */
320            *chp++ = '/';
321        }
322    }
323
324    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
325    if (fd < 0) {
326        fprintf(stderr, "%s: %s\n", path, strerror(errno));
327        exit(1);
328    }
329
330    pid_t gzip_pid = -1;
331    if (gzip_level > 0) {
332        int fds[2];
333        if (pipe(fds)) {
334            fprintf(stderr, "pipe: %s\n", strerror(errno));
335            exit(1);
336        }
337
338        fflush(redirect);
339        fflush(stdout);
340
341        gzip_pid = fork();
342        if (gzip_pid < 0) {
343            fprintf(stderr, "fork: %s\n", strerror(errno));
344            exit(1);
345        }
346
347        if (gzip_pid == 0) {
348            dup2(fds[0], STDIN_FILENO);
349            dup2(fd, STDOUT_FILENO);
350
351            close(fd);
352            close(fds[0]);
353            close(fds[1]);
354
355            char level[10];
356            snprintf(level, sizeof(level), "-%d", gzip_level);
357            execlp("gzip", "gzip", level, NULL);
358            fprintf(stderr, "exec(gzip): %s\n", strerror(errno));
359            _exit(-1);
360        }
361
362        close(fd);
363        close(fds[0]);
364        fd = fds[1];
365    }
366
367    dup2(fd, fileno(redirect));
368    close(fd);
369    return gzip_pid;
370}
371
372static bool should_dump_native_traces(const char* path) {
373    for (const char** p = native_processes_to_dump; *p; p++) {
374        if (!strcmp(*p, path)) {
375            return true;
376        }
377    }
378    return false;
379}
380
381/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
382const char *dump_traces() {
383    const char* result = NULL;
384
385    char traces_path[PROPERTY_VALUE_MAX] = "";
386    property_get("dalvik.vm.stack-trace-file", traces_path, "");
387    if (!traces_path[0]) return NULL;
388
389    /* move the old traces.txt (if any) out of the way temporarily */
390    char anr_traces_path[PATH_MAX];
391    strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
392    strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
393    if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
394        fprintf(stderr, "rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
395        return NULL;  // Can't rename old traces.txt -- no permission? -- leave it alone instead
396    }
397
398    /* make the directory if necessary */
399    char anr_traces_dir[PATH_MAX];
400    strlcpy(anr_traces_dir, traces_path, sizeof(anr_traces_dir));
401    char *slash = strrchr(anr_traces_dir, '/');
402    if (slash != NULL) {
403        *slash = '\0';
404        if (!mkdir(anr_traces_dir, 0775)) {
405            chown(anr_traces_dir, AID_SYSTEM, AID_SYSTEM);
406            chmod(anr_traces_dir, 0775);
407            if (selinux_android_restorecon(anr_traces_dir) == -1) {
408                fprintf(stderr, "restorecon failed for %s: %s\n", anr_traces_dir, strerror(errno));
409            }
410        } else if (errno != EEXIST) {
411            fprintf(stderr, "mkdir(%s): %s\n", anr_traces_dir, strerror(errno));
412            return NULL;
413        }
414    }
415
416    /* create a new, empty traces.txt file to receive stack dumps */
417    int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666);  /* -rw-rw-rw- */
418    if (fd < 0) {
419        fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
420        return NULL;
421    }
422    int chmod_ret = fchmod(fd, 0666);
423    if (chmod_ret < 0) {
424        fprintf(stderr, "fchmod on %s failed: %s\n", traces_path, strerror(errno));
425        close(fd);
426        return NULL;
427    }
428
429    /* walk /proc and kill -QUIT all Dalvik processes */
430    DIR *proc = opendir("/proc");
431    if (proc == NULL) {
432        fprintf(stderr, "/proc: %s\n", strerror(errno));
433        goto error_close_fd;
434    }
435
436    /* use inotify to find when processes are done dumping */
437    int ifd = inotify_init();
438    if (ifd < 0) {
439        fprintf(stderr, "inotify_init: %s\n", strerror(errno));
440        goto error_close_fd;
441    }
442
443    int wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
444    if (wfd < 0) {
445        fprintf(stderr, "inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
446        goto error_close_ifd;
447    }
448
449    struct dirent *d;
450    int dalvik_found = 0;
451    while ((d = readdir(proc))) {
452        int pid = atoi(d->d_name);
453        if (pid <= 0) continue;
454
455        char path[PATH_MAX];
456        char data[PATH_MAX];
457        snprintf(path, sizeof(path), "/proc/%d/exe", pid);
458        ssize_t len = readlink(path, data, sizeof(data) - 1);
459        if (len <= 0) {
460            continue;
461        }
462        data[len] = '\0';
463
464        if (!strcmp(data, "/system/bin/app_process")) {
465            /* skip zygote -- it won't dump its stack anyway */
466            snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
467            int fd = open(path, O_RDONLY);
468            len = read(fd, data, sizeof(data) - 1);
469            close(fd);
470            if (len <= 0) {
471                continue;
472            }
473            data[len] = '\0';
474            if (!strcmp(data, "zygote")) {
475                continue;
476            }
477
478            ++dalvik_found;
479            if (kill(pid, SIGQUIT)) {
480                fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
481                continue;
482            }
483
484            /* wait for the writable-close notification from inotify */
485            struct pollfd pfd = { ifd, POLLIN, 0 };
486            int ret = poll(&pfd, 1, 200);  /* 200 msec timeout */
487            if (ret < 0) {
488                fprintf(stderr, "poll: %s\n", strerror(errno));
489            } else if (ret == 0) {
490                fprintf(stderr, "warning: timed out dumping pid %d\n", pid);
491            } else {
492                struct inotify_event ie;
493                read(ifd, &ie, sizeof(ie));
494            }
495        } else if (should_dump_native_traces(data)) {
496            /* dump native process if appropriate */
497            if (lseek(fd, 0, SEEK_END) < 0) {
498                fprintf(stderr, "lseek: %s\n", strerror(errno));
499            } else {
500                dump_backtrace_to_file(pid, fd);
501            }
502        }
503    }
504
505    if (dalvik_found == 0) {
506        fprintf(stderr, "Warning: no Dalvik processes found to dump stacks\n");
507    }
508
509    static char dump_traces_path[PATH_MAX];
510    strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
511    strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
512    if (rename(traces_path, dump_traces_path)) {
513        fprintf(stderr, "rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
514        goto error_close_ifd;
515    }
516    result = dump_traces_path;
517
518    /* replace the saved [ANR] traces.txt file */
519    rename(anr_traces_path, traces_path);
520
521error_close_ifd:
522    close(ifd);
523error_close_fd:
524    close(fd);
525    return result;
526}
527
528void play_sound(const char* path) {
529    run_command(NULL, 5, "/system/bin/stagefright", "-o", "-a", path, NULL);
530}
531