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 <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 
     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 
     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 */
    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 */
    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         fflush(stdout);
    171         _exit(-1);
    172     }
    173 
    174     /* handle parent case */
    175     for (;;) {
    176         int status;
    177         pid_t p = waitpid(pid, &status, WNOHANG);
    178         float elapsed = (float) (clock() - start) / CLOCKS_PER_SEC;
    179         if (p == pid) {
    180             if (WIFSIGNALED(status)) {
    181                 printf("*** %s: Killed by signal %d\n", command, WTERMSIG(status));
    182             } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
    183                 printf("*** %s: Exit code %d\n", command, WEXITSTATUS(status));
    184             }
    185             if (title) printf("[%s: %.1fs elapsed]\n\n", command, elapsed);
    186             return status;
    187         }
    188 
    189         if (timeout_seconds && elapsed > timeout_seconds) {
    190             printf("*** %s: Timed out after %.1fs (killing pid %d)\n", command, elapsed, pid);
    191             kill(pid, SIGTERM);
    192             return -1;
    193         }
    194 
    195         usleep(100000);  // poll every 0.1 sec
    196     }
    197 }
    198 
    199 size_t num_props = 0;
    200 static char* props[2000];
    201 
    202 static void print_prop(const char *key, const char *name, void *user) {
    203     (void) user;
    204     if (num_props < sizeof(props) / sizeof(props[0])) {
    205         char buf[PROPERTY_KEY_MAX + PROPERTY_VALUE_MAX + 10];
    206         snprintf(buf, sizeof(buf), "[%s]: [%s]\n", key, name);
    207         props[num_props++] = strdup(buf);
    208     }
    209 }
    210 
    211 static int compare_prop(const void *a, const void *b) {
    212     return strcmp(*(char * const *) a, *(char * const *) b);
    213 }
    214 
    215 /* prints all the system properties */
    216 void print_properties() {
    217     size_t i;
    218     num_props = 0;
    219     property_list(print_prop, NULL);
    220     qsort(&props, num_props, sizeof(props[0]), compare_prop);
    221 
    222     printf("------ SYSTEM PROPERTIES ------\n");
    223     for (i = 0; i < num_props; ++i) {
    224         fputs(props[i], stdout);
    225         free(props[i]);
    226     }
    227     printf("\n");
    228 }
    229 
    230 /* redirect output to a service control socket */
    231 void redirect_to_socket(FILE *redirect, const char *service) {
    232     int s = android_get_control_socket(service);
    233     if (s < 0) {
    234         fprintf(stderr, "android_get_control_socket(%s): %s\n", service, strerror(errno));
    235         exit(1);
    236     }
    237     if (listen(s, 4) < 0) {
    238         fprintf(stderr, "listen(control socket): %s\n", strerror(errno));
    239         exit(1);
    240     }
    241 
    242     struct sockaddr addr;
    243     socklen_t alen = sizeof(addr);
    244     int fd = accept(s, &addr, &alen);
    245     if (fd < 0) {
    246         fprintf(stderr, "accept(control socket): %s\n", strerror(errno));
    247         exit(1);
    248     }
    249 
    250     fflush(redirect);
    251     dup2(fd, fileno(redirect));
    252     close(fd);
    253 }
    254 
    255 /* redirect output to a file, optionally gzipping; returns gzip pid (or -1) */
    256 pid_t redirect_to_file(FILE *redirect, char *path, int gzip_level) {
    257     char *chp = path;
    258 
    259     /* skip initial slash */
    260     if (chp[0] == '/')
    261         chp++;
    262 
    263     /* create leading directories, if necessary */
    264     while (chp && chp[0]) {
    265         chp = strchr(chp, '/');
    266         if (chp) {
    267             *chp = 0;
    268             mkdir(path, 0775);  /* drwxrwxr-x */
    269             *chp++ = '/';
    270         }
    271     }
    272 
    273     int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    274     if (fd < 0) {
    275         fprintf(stderr, "%s: %s\n", path, strerror(errno));
    276         exit(1);
    277     }
    278 
    279     pid_t gzip_pid = -1;
    280     if (gzip_level > 0) {
    281         int fds[2];
    282         if (pipe(fds)) {
    283             fprintf(stderr, "pipe: %s\n", strerror(errno));
    284             exit(1);
    285         }
    286 
    287         fflush(redirect);
    288         fflush(stdout);
    289 
    290         gzip_pid = fork();
    291         if (gzip_pid < 0) {
    292             fprintf(stderr, "fork: %s\n", strerror(errno));
    293             exit(1);
    294         }
    295 
    296         if (gzip_pid == 0) {
    297             dup2(fds[0], STDIN_FILENO);
    298             dup2(fd, STDOUT_FILENO);
    299 
    300             close(fd);
    301             close(fds[0]);
    302             close(fds[1]);
    303 
    304             char level[10];
    305             snprintf(level, sizeof(level), "-%d", gzip_level);
    306             execlp("gzip", "gzip", level, NULL);
    307             fprintf(stderr, "exec(gzip): %s\n", strerror(errno));
    308             _exit(-1);
    309         }
    310 
    311         close(fd);
    312         close(fds[0]);
    313         fd = fds[1];
    314     }
    315 
    316     dup2(fd, fileno(redirect));
    317     close(fd);
    318     return gzip_pid;
    319 }
    320 
    321 /* dump Dalvik stack traces, return the trace file location (NULL if none) */
    322 const char *dump_vm_traces() {
    323     char traces_path[PROPERTY_VALUE_MAX] = "";
    324     property_get("dalvik.vm.stack-trace-file", traces_path, "");
    325     if (!traces_path[0]) return NULL;
    326 
    327     /* move the old traces.txt (if any) out of the way temporarily */
    328     char anr_traces_path[PATH_MAX];
    329     strlcpy(anr_traces_path, traces_path, sizeof(anr_traces_path));
    330     strlcat(anr_traces_path, ".anr", sizeof(anr_traces_path));
    331     if (rename(traces_path, anr_traces_path) && errno != ENOENT) {
    332         fprintf(stderr, "rename(%s, %s): %s\n", traces_path, anr_traces_path, strerror(errno));
    333         return NULL;  // Can't rename old traces.txt -- no permission? -- leave it alone instead
    334     }
    335 
    336     /* make the directory if necessary */
    337     char anr_traces_dir[PATH_MAX];
    338     strlcpy(anr_traces_dir, traces_path, sizeof(anr_traces_dir));
    339     char *slash = strrchr(anr_traces_dir, '/');
    340     if (slash != NULL) {
    341         *slash = '\0';
    342         if (!mkdir(anr_traces_dir, 0775)) {
    343             chown(anr_traces_dir, AID_SYSTEM, AID_SYSTEM);
    344         } else if (errno != EEXIST) {
    345             fprintf(stderr, "mkdir(%s): %s\n", anr_traces_dir, strerror(errno));
    346             return NULL;
    347         }
    348     }
    349 
    350     /* create a new, empty traces.txt file to receive stack dumps */
    351     int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC, 0666);  /* -rw-rw-rw- */
    352     if (fd < 0) {
    353         fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
    354         return NULL;
    355     }
    356     close(fd);
    357 
    358     /* walk /proc and kill -QUIT all Dalvik processes */
    359     DIR *proc = opendir("/proc");
    360     if (proc == NULL) {
    361         fprintf(stderr, "/proc: %s\n", strerror(errno));
    362         return NULL;
    363     }
    364 
    365     /* use inotify to find when processes are done dumping */
    366     int ifd = inotify_init();
    367     if (ifd < 0) {
    368         fprintf(stderr, "inotify_init: %s\n", strerror(errno));
    369         return NULL;
    370     }
    371 
    372     int wfd = inotify_add_watch(ifd, traces_path, IN_CLOSE_WRITE);
    373     if (wfd < 0) {
    374         fprintf(stderr, "inotify_add_watch(%s): %s\n", traces_path, strerror(errno));
    375         return NULL;
    376     }
    377 
    378     struct dirent *d;
    379     int dalvik_found = 0;
    380     while ((d = readdir(proc))) {
    381         int pid = atoi(d->d_name);
    382         if (pid <= 0) continue;
    383 
    384         /* identify Dalvik: /proc/(pid)/exe = /system/bin/app_process */
    385         char path[PATH_MAX], data[PATH_MAX];
    386         snprintf(path, sizeof(path), "/proc/%d/exe", pid);
    387         size_t len = readlink(path, data, sizeof(data) - 1);
    388         if (len <= 0 || memcmp(data, "/system/bin/app_process", 23)) continue;
    389 
    390         /* skip zygote -- it won't dump its stack anyway */
    391         snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
    392         int fd = open(path, O_RDONLY);
    393         len = read(fd, data, sizeof(data) - 1);
    394         close(fd);
    395         if (len <= 0 || !memcmp(data, "zygote", 6)) continue;
    396 
    397         ++dalvik_found;
    398         if (kill(pid, SIGQUIT)) {
    399             fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
    400             continue;
    401         }
    402 
    403         /* wait for the writable-close notification from inotify */
    404         struct pollfd pfd = { ifd, POLLIN, 0 };
    405         int ret = poll(&pfd, 1, 200);  /* 200 msec timeout */
    406         if (ret < 0) {
    407             fprintf(stderr, "poll: %s\n", strerror(errno));
    408         } else if (ret == 0) {
    409             fprintf(stderr, "warning: timed out dumping pid %d\n", pid);
    410         } else {
    411             struct inotify_event ie;
    412             read(ifd, &ie, sizeof(ie));
    413         }
    414     }
    415 
    416     close(ifd);
    417     if (dalvik_found == 0) {
    418         fprintf(stderr, "Warning: no Dalvik processes found to dump stacks\n");
    419     }
    420 
    421     static char dump_traces_path[PATH_MAX];
    422     strlcpy(dump_traces_path, traces_path, sizeof(dump_traces_path));
    423     strlcat(dump_traces_path, ".bugreport", sizeof(dump_traces_path));
    424     if (rename(traces_path, dump_traces_path)) {
    425         fprintf(stderr, "rename(%s, %s): %s\n", traces_path, dump_traces_path, strerror(errno));
    426         return NULL;
    427     }
    428 
    429     /* replace the saved [ANR] traces.txt file */
    430     rename(anr_traces_path, traces_path);
    431     return dump_traces_path;
    432 }
    433 
    434 void play_sound(const char* path) {
    435     run_command(NULL, 5, "/system/bin/stagefright", "-o", "-a", path, NULL);
    436 }
    437