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