diff options
Diffstat (limited to 'cmds/dumpstate')
-rw-r--r-- | cmds/dumpstate/Android.mk | 17 | ||||
-rwxr-xr-x | cmds/dumpstate/dumpstate | 114 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.c | 291 | ||||
-rw-r--r-- | cmds/dumpstate/dumpstate.h | 120 | ||||
-rw-r--r-- | cmds/dumpstate/utils.c | 230 |
5 files changed, 654 insertions, 118 deletions
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk index e101aeb..f61d7ec 100644 --- a/cmds/dumpstate/Android.mk +++ b/cmds/dumpstate/Android.mk @@ -1,11 +1,18 @@ +ifneq ($(TARGET_SIMULATOR),true) + LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -ALL_PREBUILT += $(TARGET_OUT)/bin/dumpstate -$(TARGET_OUT)/bin/dumpstate : $(LOCAL_PATH)/dumpstate | $(ACP) - $(transform-prebuilt-to-target) +LOCAL_SRC_FILES:= dumpstate.c utils.c + +LOCAL_MODULE:= dumpstate + +LOCAL_SHARED_LIBRARIES := libcutils -SYMLINKS := $(TARGET_OUT_EXECUTABLES)/dumpcrash +include $(BUILD_EXECUTABLE) + +COMMANDS = dumpcrash bugreport +SYMLINKS := $(addprefix $(TARGET_OUT_EXECUTABLES)/,$(COMMANDS)) $(SYMLINKS): DUMPSTATE_BINARY := dumpstate $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk @echo "Symlink: $@ -> $(DUMPSTATE_BINARY)" @@ -14,3 +21,5 @@ $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk $(hide) ln -sf $(DUMPSTATE_BINARY) $@ ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS) + +endif diff --git a/cmds/dumpstate/dumpstate b/cmds/dumpstate/dumpstate deleted file mode 100755 index 8dd7d14..0000000 --- a/cmds/dumpstate/dumpstate +++ /dev/null @@ -1,114 +0,0 @@ -#!/system/bin/sh - -# Dumps to /tmp/state by default. -# Specify "-" to dump to stdout, or any file name to dump to that file. - -crashmode=0 -case $0 in - dumpcrash|*/dumpcrash) crashmode=1 ;; -esac - -case $1 in - "") echo "DUMPING STATE TO /tmp/state"; exec 1>/tmp/state ;; - -) ;; - *) echo "DUMPING STATE TO $1"; exec 1>$1 ;; -esac - -case $crashmode in 0) - echo "========================================================" - echo "== dumpstate" - echo "========================================================" - echo "------ SYSTEM LOG ------" - logcat -v time -d '*:v' - echo "------ VM TRACES ------" - cat /data/anr/traces.txt - echo "------ EVENT LOG TAGS ------" - cat /etc/event-log-tags - echo "------ EVENT LOG ------" - logcat -b events -v time -d '*:v' - echo "------ RADIO LOG ------" - logcat -b radio -v time -d '*:v' - echo "------ NETWORK STATE ------" - echo "Interfaces:" - netcfg - echo "" - echo "Routes:" - cat /proc/net/route - echo "------ SYSTEM PROPERTIES ------" - getprop - echo "------ KERNEL LOG ------" - dmesg - echo "------ KERNEL WAKELOCKS ------" - cat /proc/wakelocks - echo "" - echo "------ PROCESSES ------" - ps - echo "------ PROCESSES AND THREADS ------" - ps -t -p - echo "------ MEMORY INFO ------" - cat /proc/meminfo - echo "------ PSS INFO ------" - top -n 1 -d 0 -m 15 -s pss - echo "------ PROCRANK ------" - procrank - echo "------ LIBRANK ------" - librank - echo "------ VIRTUAL MEMORY STATS ------" - cat /proc/vmstat - echo "------ SLAB INFO ------" - cat /proc/slabinfo - echo "------ ZONEINFO ------" - cat /proc/zoneinfo - echo "------ BINDER FAILED TRANSACTION LOG ------" - cat /proc/binder/failed_transaction_log - echo "" - echo "------ BINDER TRANSACTION LOG ------" - cat /proc/binder/transaction_log - echo "" - echo "------ BINDER TRANSACTIONS ------" - cat /proc/binder/transactions - echo "" - echo "------ BINDER STATS ------" - cat /proc/binder/stats - echo "" - for i in `ls /proc/binder/proc`; do - echo "------ BINDER PROCESS STATE: $i ------" - cat /proc/binder/proc/$i - echo "" - done - echo "------ FILESYSTEMS ------" - df - echo "------ PACKAGE SETTINGS ------" - cat /data/system/packages.xml - echo "------ PACKAGE UID ERRORS ------" - cat /data/system/uiderrors.txt - echo "------ LAST KERNEL LOG ------" - cat /proc/last_kmsg -;; esac - -echo "========================================================" -echo "== build.prop" -echo "========================================================" - -# the crash server parses key-value pairs between the VERSION INFO and -# END lines so we can aggregate crash reports based on this data. -echo "------ VERSION INFO ------" -echo "currenttime=`date`" -echo "kernel.version=`cat /proc/version`" -echo "kernel.cmdline=`cat /proc/cmdline`" -cat /system/build.prop -echo "gsm.version.ril-impl=`getprop gsm.version.ril-impl`" -echo "gsm.version.baseband=`getprop gsm.version.baseband`" -echo "gsm.imei=`getprop gsm.imei`" -echo "gsm.sim.operator.numeric=`getprop gsm.sim.operator.numeric`" -echo "gsm.operator.alpha=`getprop gsm.operator.alpha`" -echo "------ END ------" - -case $crashmode in 0) - echo "========================================================" - echo "== dumpsys" - echo "========================================================" - dumpsys -;; esac - -exit 0 diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c new file mode 100644 index 0000000..dea269d --- /dev/null +++ b/cmds/dumpstate/dumpstate.c @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/stat.h> +#include <limits.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include "private/android_filesystem_config.h" + +#include "dumpstate.h" + +static char* const gzip_args[] = { "gzip", "-6", 0 }; +static int start_pattern[] = { 150, 0 }; +static int end_pattern[] = { 75, 50, 75, 50, 75, 0 }; + +static struct tm now; + +/* dumps the current system state to stdout */ +static void dumpstate(int full) { + if (full) { + PRINT("========================================================"); + PRINT("== dumpstate"); + PRINT("========================================================"); + PRINT("------ SYSTEM LOG ------"); + EXEC4("logcat", "-v", "time", "-d", "*:v"); + PRINT("------ VM TRACES ------"); + DUMP("/data/anr/traces.txt"); + PRINT("------ EVENT LOG TAGS ------"); + DUMP("/etc/event-log-tags"); + PRINT("------ EVENT LOG ------"); + EXEC6("logcat", "-b", "events", "-v", "time", "-d", "*:v"); + PRINT("------ RADIO LOG ------"); + EXEC6("logcat", "-b", "radio", "-v", "time", "-d", "*:v"); + PRINT("------ NETWORK STATE ------"); + PRINT("Interfaces:"); + EXEC("netcfg"); + PRINT(""); + PRINT("Routes:"); + DUMP("/proc/net/route"); + PRINT("------ SYSTEM PROPERTIES ------"); + print_properties(); + PRINT("------ KERNEL LOG ------"); + EXEC("dmesg"); + PRINT("------ KERNEL WAKELOCKS ------"); + DUMP("/proc/wakelocks"); + PRINT(""); + PRINT("------ PROCESSES ------"); + EXEC("ps"); + PRINT("------ PROCESSES AND THREADS ------"); + EXEC2("ps", "-t", "-p"); + PRINT("------ MEMORY INFO ------"); + DUMP("/proc/meminfo"); + PRINT("------ PSS INFO ------"); + EXEC8("top", "-n", "1", "-d", "0", "-m", "15", "-s", "pss"); + PRINT("------ PROCRANK ------"); + EXEC("procrank"); + PRINT("------ LIBRANK ------"); + EXEC("librank"); + PRINT("------ VIRTUAL MEMORY STATS ------"); + DUMP("/proc/vmstat"); + PRINT("------ SLAB INFO ------"); + DUMP("/proc/slabinfo"); + PRINT("------ ZONEINFO ------"); + DUMP("/proc/zoneinfo"); + PRINT("------ BINDER FAILED TRANSACTION LOG ------"); + DUMP("/proc/binder/failed_transaction_log"); + PRINT(""); + PRINT("------ BINDER TRANSACTION LOG ------"); + DUMP("/proc/binder/transaction_log"); + PRINT(""); + PRINT("------ BINDER TRANSACTIONS ------"); + DUMP("/proc/binder/transactions"); + PRINT(""); + PRINT("------ BINDER STATS ------"); + DUMP("/proc/binder/stats"); + PRINT(""); + PRINT("------ BINDER PROCESS STATE: $i ------"); + DUMP_FILES("/proc/binder/proc"); + PRINT("------ FILESYSTEMS ------"); + EXEC("df"); + PRINT("------ PACKAGE SETTINGS ------"); + DUMP("/data/system/packages.xml"); + PRINT("------ PACKAGE UID ERRORS ------"); + DUMP("/data/system/uiderrors.txt"); + PRINT("------ LAST KERNEL LOG ------"); + DUMP("/proc/last_kmsg"); + } + PRINT("========================================================"); + PRINT("== build.prop"); + PRINT("========================================================"); + + /* the crash server parses key-value pairs between the VERSION INFO and + * END lines so we can aggregate crash reports based on this data. + */ + PRINT("------ VERSION INFO ------"); + print_date("currenttime=", &now); + DUMP_PROMPT("kernel.version=", "/proc/version"); + DUMP_PROMPT("kernel.cmdline=", "/proc/cmdline"); + DUMP("/system/build.prop"); + PROPERTY("gsm.version.ril-impl"); + PROPERTY("gsm.version.baseband"); + PROPERTY("gsm.imei"); + PROPERTY("gsm.sim.operator.numeric"); + PROPERTY("gsm.operator.alpha"); + PRINT("------ END ------"); + + if (full) { + PRINT("========================================================"); + PRINT("== dumpsys"); + PRINT("========================================================"); + EXEC("dumpsys"); + } +} + +/* used to check the file name passed via argv[0] */ +static int check_command_name(const char* name, const char* test) { + int name_length, test_length; + + if (!strcmp(name, test)) + return 1; + + name_length = strlen(name); + test_length = strlen(test); + + if (name_length > test_length + 2) { + name += (name_length - test_length); + if (name[-1] != '/') + return 0; + if (!strcmp(name, test)) + return 1; + } + + return 0; +} + +int main(int argc, char *argv[]) { + int dumpcrash = check_command_name(argv[0], "dumpcrash"); + int bugreport = check_command_name(argv[0], "bugreport"); + int add_date = 0; + char* outfile = 0; + int vibrate = 0; + int compress = 0; + int c, fd, vibrate_fd, fds[2]; + char path[PATH_MAX]; + pid_t pid; + + /* set as high priority, and protect from OOM killer */ + setpriority(PRIO_PROCESS, 0, -20); + protect_from_oom_killer(); + + get_time(&now); + + if (bugreport) { + do { + c = getopt(argc, argv, "do:vz"); + if (c == EOF) + break; + switch (c) { + case 'd': + add_date = 1; + break; + case 'o': + outfile = optarg; + break; + case 'v': + vibrate = 1; + break; + case 'z': + compress = 1; + break; + case '?': + fprintf(stderr, "%s: invalid option -%c\n", + argv[0], optopt); + exit(1); + } + } while (1); + } + + /* open vibrator before switching user */ + if (vibrate) { + vibrate_fd = open("/sys/class/timed_output/vibrator/enable", O_WRONLY); + if (vibrate_fd > 0) + fcntl(vibrate_fd, F_SETFD, FD_CLOEXEC); + } else + vibrate_fd = -1; + + /* switch to non-root user and group */ + setgid(AID_LOG); + setuid(AID_SHELL); + + /* make it safe to use both printf and STDOUT_FILENO */ + setvbuf(stdout, 0, _IONBF, 0); + + if (outfile) { + if (strlen(outfile) > sizeof(path) - 100) + exit(1); + + strcpy(path, outfile); + if (add_date) { + char date[260]; + strftime(date, sizeof(date), + "-%Y-%m-%d-%H-%M-%S", + &now); + strcat(path, date); + } + if (compress) + strcat(path, ".gz"); + + /* ensure that all directories in the path exist */ + create_directories(path); + fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) + return fd; + + if (compress) { + pipe(fds); + + /* redirect our stdout to the pipe */ + dup2(fds[1], STDOUT_FILENO); + close(fds[1]); + + if ((pid = fork()) < 0) + { + fprintf(stderr, "fork error\n"); + exit(1); + } + + if (pid) { + /* parent case */ + + /* close our copy of the input to gzip */ + close(fds[0]); + /* close our copy of the output file */ + close(fd); + } else { + /* child case */ + + /* redirect our input pipe to stdin */ + dup2(fds[0], STDIN_FILENO); + close(fds[0]); + + /* redirect stdout to the output file */ + dup2(fd, STDOUT_FILENO); + close(fd); + + /* run gzip to postprocess our output */ + execv("/system/bin/gzip", gzip_args); + fprintf(stderr, "execv returned\n"); + } + } else { + /* redirect stdout to the output file */ + dup2(fd, STDOUT_FILENO); + close(fd); + } + } + /* else everything will print to stdout */ + + if (vibrate) { + vibrate_pattern(vibrate_fd, start_pattern); + } + dumpstate(!dumpcrash); + if (vibrate) { + vibrate_pattern(vibrate_fd, end_pattern); + close(vibrate_fd); + } + + /* so gzip will terminate */ + close(STDOUT_FILENO); + + return 0; +} + diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h new file mode 100644 index 0000000..b956f99 --- /dev/null +++ b/cmds/dumpstate/dumpstate.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DUMPSTATE_H_ +#define _DUMPSTATE_H_ + +#include <time.h> + +// Commands time out after 15 seconds +#define TIMEOUT 15 + +#define PRINT(s) printf("%s\n", s) + +#define DUMP(file) dump_file(file) + +#define DUMP_FILES(path) dump_files(path) + +#define DUMP_PROMPT(prompt, file) \ +{ \ + printf(prompt); \ + dump_file(file); \ +} + +#define EXEC(cmd) \ +{ \ + static struct Command c = { \ + "/system/bin/" cmd, \ + { cmd, 0 } \ + }; \ + run_command(&c, TIMEOUT); \ +} + +#define EXEC2(cmd, a1, a2) \ +{ \ + static struct Command c = { \ + "/system/bin/" cmd, \ + { cmd, a1, a2, 0 } \ + }; \ + run_command(&c, TIMEOUT); \ +} + +#define EXEC4(cmd, a1, a2, a3, a4) \ +{ \ + static struct Command c = { \ + "/system/bin/" cmd, \ + { cmd, a1, a2, a3, a4, 0 } \ + }; \ + run_command(&c, TIMEOUT); \ +} + +#define EXEC6(cmd, a1, a2, a3, a4, a5, a6) \ +{ \ + static struct Command c = { \ + "/system/bin/" cmd, \ + { cmd, a1, a2, a3, a4, a5, a6, 0 } \ + }; \ + run_command(&c, TIMEOUT); \ +} + +#define EXEC8(cmd, a1, a2, a3, a4, a5, a6, a7, a8) \ +{ \ + static struct Command c = { \ + "/system/bin/" cmd, \ + { cmd, a1, a2, a3, a4, a5, a6, a7, a8, 0 } \ + }; \ + run_command(&c, TIMEOUT); \ +} + +#define PROPERTY(name) print_property(name) + +struct Command { + const char* path; + char* const args[]; +}; +typedef struct Command Command; + +/* prints the contents of a file */ +int dump_file(const char* path); + +/* prints the contents of all files in a directory */ +void dump_files(const char* path); + +/* forks a command and waits for it to finish */ +int run_command(struct Command* cmd, int timeout); + +/* reads the current time into tm */ +void get_time(struct tm *tm); + +/* prints the date in tm */ +void print_date(const char* prompt, struct tm *tm); + +/* prints the name and value of a system property */ +int print_property(const char* name); + +/* prints all the system properties */ +void print_properties(); + +/* creates directories as needed for the given path */ +void create_directories(char *path); + +/* runs the vibrator using the given pattern */ +void vibrate_pattern(int fd, int* pattern); + +/* prevents the OOM killer from killing us */ +void protect_from_oom_killer(); + +#endif /* _DUMPSTATE_H_ */ diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c new file mode 100644 index 0000000..60d845f --- /dev/null +++ b/cmds/dumpstate/utils.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <sys/stat.h> +#include <dirent.h> +#include <limits.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/wait.h> + +#include <cutils/properties.h> +#include <sys/system_properties.h> + +#include "dumpstate.h" + + +/* prints the contents of a file */ +int dump_file(const char* path) { + char buffer[32768]; + int fd, amount_read; + int ret = 0; + + fd = open(path, O_RDONLY); + if (fd < 0) + return fd; + + do { + ret = read(fd, buffer, sizeof(buffer)); + if (ret > 0) + ret = write(STDOUT_FILENO, buffer, ret); + } while (ret > 0); + + buffer[0] = '\n'; + write(STDOUT_FILENO, buffer, 1); + + close(fd); + return ret; +} + +/* prints the contents of all files in a directory */ +void dump_files(const char* path) { + DIR* dir; + struct dirent* entry; + char buffer[PATH_MAX]; + + dir = opendir(path); + if (!dir) { + fprintf(stderr, "could not open directory %s\n", path); + return; + } + + while ((entry = readdir(dir))) { + if (entry->d_type == DT_REG) { + snprintf(buffer, sizeof(buffer), "%s/%s", path, entry->d_name); + dump_file(path); + printf("\n"); + } + } + + closedir(dir); +} + +/* prints the name and value of a system property */ +int print_property(const char* name) { + char value[PROP_VALUE_MAX]; + + __system_property_get(name, value); + printf("%s=%s\n", name, value); + return 0; +} + +static pid_t alarm_pid = 0; +static int timed_out = 0; +static void sig_alarm(int sig) +{ + if (alarm_pid) { + kill(alarm_pid, SIGKILL); + timed_out = 1; + alarm_pid = 0; + } +} + +/* forks a command and waits for it to finish */ +int run_command(struct Command* cmd, int timeout) { + struct sigaction sa; + pid_t pid; + int status; + + pid = fork(); + /* handle error case */ + if (pid < 0) + return pid; + + /* handle child case */ + if (pid == 0) { + int ret = execv(cmd->path, cmd->args); + if (ret) + fprintf(stderr, "execv %s returned %d\n", cmd->path, ret); + exit(ret); + } + + /* handle parent case */ + timed_out = 0; + if (timeout) { + memset(&sa, 0, sizeof(sa)); + sa.sa_flags = SA_RESETHAND; + sa.sa_handler = sig_alarm; + sigaction(SIGALRM, &sa, NULL); + + /* set an alarm so we don't hang forever */ + alarm_pid = pid; + alarm(timeout); + } + + waitpid(pid, &status, 0); + + if (timed_out) + printf("ERROR: command %s timed out\n", cmd->path); + + return status; +} + +/* reads the current time into tm */ +void get_time(struct tm *tm) { + time_t t; + + tzset(); + time(&t); + localtime_r(&t, tm); +} + +/* prints the date in tm */ +void print_date(const char* prompt, struct tm *tm) { + char strbuf[260]; + + strftime(strbuf, sizeof(strbuf), + "%a %b %e %H:%M:%S %Z %Y", + tm); + printf("%s%s\n", prompt, strbuf); +} + + +static void print_prop(const char *key, const char *name, + void *user __attribute__((unused))) +{ + printf("[%s]: [%s]\n", key, name); +} + +/* prints all the system properties */ +void print_properties() { + property_list(print_prop, NULL); +} + +/* creates directories as needed for the given path */ +void create_directories(char *path) +{ + char *chp = path; + + /* skip initial slash */ + if (chp[0] == '/') + chp++; + + while (chp && chp[0]) { + chp = strchr(chp, '/'); + if (chp) { + *chp = 0; + mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + *chp = '/'; + chp++; + } + } +} + +/* runs the vibrator using the given pattern */ +void vibrate_pattern(int fd, int* pattern) +{ + struct timespec tm; + char buffer[10]; + + while (*pattern) { + /* read vibrate on time */ + int on_time = *pattern++; + snprintf(buffer, sizeof(buffer), "%d", on_time); + write(fd, buffer, strlen(buffer)); + + /* read vibrate off time */ + int delay = *pattern++; + if (delay) { + delay += on_time; + + tm.tv_sec = delay / 1000; + tm.tv_nsec = (delay % 1000) * 1000000; + nanosleep(&tm, NULL); + } else + break; + } +} + +/* prevents the OOM killer from killing us */ +void protect_from_oom_killer() +{ + int fd; + + fd = open("/proc/self/oom_adj", O_WRONLY); + if (fd >= 0) { + // -17 should make us immune to OOM + const char* text = "-17"; + write(fd, text, strlen(text)); + close(fd); + } +} |