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