diff options
Diffstat (limited to 'cmds/dumpstate/dumpstate.c')
-rw-r--r-- | cmds/dumpstate/dumpstate.c | 291 |
1 files changed, 291 insertions, 0 deletions
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; +} + |