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