Add timeout for dump_file.
It turns out dump_file is used on a number of /proc and system files. In one case, the read of a file stalled and caused a bugreport to hang forever. It's still possible if there is a kernel bug that this could stall forever, but less likely. Also, change the return type of nanotime to uint64_t. Testing: - Created a named fifo and verified that dump_file fails with a timeout. - Created a large /data/anr/traces.txt to verify that large files still dump properly and that the additional NONBLOCK parameter doesn't cause a problem. - Created a dummy /data/tombstones/tombstone_00 to verify that the dump of these files still works. - Compared a dump using the old dumpstate to the new dumpstate to verify nothing obviously different. Bug: 19117030 Change-Id: I0d3dd27583c853cdaccd2fd278748cb5f9ccd4fb
This commit is contained in:
parent
d80268ba19
commit
54bcc5ffd5
@ -63,7 +63,8 @@ static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
|
|||||||
time_t thirty_minutes_ago = time(NULL) - 60*30;
|
time_t thirty_minutes_ago = time(NULL) - 60*30;
|
||||||
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
|
for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
|
||||||
snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
|
snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
|
||||||
int fd = open(data[i].name, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
|
int fd = TEMP_FAILURE_RETRY(open(data[i].name,
|
||||||
|
O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
|
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) &&
|
||||||
(time_t) st.st_mtime >= thirty_minutes_ago) {
|
(time_t) st.st_mtime >= thirty_minutes_ago) {
|
||||||
@ -159,7 +160,8 @@ static void dumpstate() {
|
|||||||
if (!anr_traces_path[0]) {
|
if (!anr_traces_path[0]) {
|
||||||
printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
|
printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
|
||||||
} else {
|
} else {
|
||||||
int fd = open(anr_traces_path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
|
int fd = TEMP_FAILURE_RETRY(open(anr_traces_path,
|
||||||
|
O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
|
printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path, strerror(errno));
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,7 +31,9 @@ typedef void (for_each_userid_func)(int);
|
|||||||
/* prints the contents of a file */
|
/* prints the contents of a file */
|
||||||
int dump_file(const char *title, const char *path);
|
int dump_file(const char *title, const char *path);
|
||||||
|
|
||||||
/* prints the contents of the fd */
|
/* prints the contents of the fd
|
||||||
|
* fd must have been opened with the flag O_NONBLOCK.
|
||||||
|
*/
|
||||||
int dump_file_from_fd(const char *title, const char *path, int fd);
|
int dump_file_from_fd(const char *title, const char *path, int fd);
|
||||||
|
|
||||||
/* forks a command and waits for it to finish -- terminate args with NULL */
|
/* forks a command and waits for it to finish -- terminate args with NULL */
|
||||||
|
@ -53,6 +53,12 @@ static const char* native_processes_to_dump[] = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static uint64_t nanotime() {
|
||||||
|
struct timespec ts;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
return (uint64_t)ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
void for_each_userid(void (*func)(int), const char *header) {
|
void for_each_userid(void (*func)(int), const char *header) {
|
||||||
DIR *d;
|
DIR *d;
|
||||||
struct dirent *de;
|
struct dirent *de;
|
||||||
@ -98,7 +104,7 @@ static void __for_each_pid(void (*helper)(int, const char *, void *), const char
|
|||||||
|
|
||||||
sprintf(cmdpath,"/proc/%d/cmdline", pid);
|
sprintf(cmdpath,"/proc/%d/cmdline", pid);
|
||||||
memset(cmdline, 0, sizeof(cmdline));
|
memset(cmdline, 0, sizeof(cmdline));
|
||||||
if ((fd = open(cmdpath, O_RDONLY)) < 0) {
|
if ((fd = TEMP_FAILURE_RETRY(open(cmdpath, O_RDONLY))) < 0) {
|
||||||
strcpy(cmdline, "N/A");
|
strcpy(cmdline, "N/A");
|
||||||
} else {
|
} else {
|
||||||
read(fd, cmdline, sizeof(cmdline) - 1);
|
read(fd, cmdline, sizeof(cmdline) - 1);
|
||||||
@ -149,7 +155,7 @@ static void for_each_tid_helper(int pid, const char *cmdline, void *arg) {
|
|||||||
|
|
||||||
sprintf(commpath,"/proc/%d/comm", tid);
|
sprintf(commpath,"/proc/%d/comm", tid);
|
||||||
memset(comm, 0, sizeof(comm));
|
memset(comm, 0, sizeof(comm));
|
||||||
if ((fd = open(commpath, O_RDONLY)) < 0) {
|
if ((fd = TEMP_FAILURE_RETRY(open(commpath, O_RDONLY))) < 0) {
|
||||||
strcpy(comm, "N/A");
|
strcpy(comm, "N/A");
|
||||||
} else {
|
} else {
|
||||||
char *c;
|
char *c;
|
||||||
@ -180,7 +186,7 @@ void show_wchan(int pid, int tid, const char *name) {
|
|||||||
memset(buffer, 0, sizeof(buffer));
|
memset(buffer, 0, sizeof(buffer));
|
||||||
|
|
||||||
sprintf(path, "/proc/%d/wchan", tid);
|
sprintf(path, "/proc/%d/wchan", tid);
|
||||||
if ((fd = open(path, O_RDONLY)) < 0) {
|
if ((fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY))) < 0) {
|
||||||
printf("Failed to open '%s' (%s)\n", path, strerror(errno));
|
printf("Failed to open '%s' (%s)\n", path, strerror(errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -250,22 +256,7 @@ void do_showmap(int pid, const char *name) {
|
|||||||
run_command(title, 10, SU_PATH, "root", "showmap", arg, NULL);
|
run_command(title, 10, SU_PATH, "root", "showmap", arg, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* prints the contents of a file */
|
static int _dump_file_from_fd(const char *title, const char *path, int fd) {
|
||||||
int dump_file(const char *title, const char *path) {
|
|
||||||
int fd = open(path, O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
int err = errno;
|
|
||||||
if (title) printf("------ %s (%s) ------\n", title, path);
|
|
||||||
printf("*** %s: %s\n", path, strerror(err));
|
|
||||||
if (title) printf("\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return dump_file_from_fd(title, path, fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dump_file_from_fd(const char *title, const char *path, int fd) {
|
|
||||||
char buffer[32768];
|
|
||||||
|
|
||||||
if (title) printf("------ %s (%s", title, path);
|
if (title) printf("------ %s (%s", title, path);
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
@ -279,26 +270,76 @@ int dump_file_from_fd(const char *title, const char *path, int fd) {
|
|||||||
printf(") ------\n");
|
printf(") ------\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int newline = 0;
|
bool newline = false;
|
||||||
for (;;) {
|
fd_set read_set;
|
||||||
int ret = read(fd, buffer, sizeof(buffer));
|
struct timeval tm;
|
||||||
if (ret > 0) {
|
while (1) {
|
||||||
newline = (buffer[ret - 1] == '\n');
|
FD_ZERO(&read_set);
|
||||||
ret = fwrite(buffer, ret, 1, stdout);
|
FD_SET(fd, &read_set);
|
||||||
|
/* Timeout if no data is read for 30 seconds. */
|
||||||
|
tm.tv_sec = 30;
|
||||||
|
tm.tv_usec = 0;
|
||||||
|
uint64_t elapsed = nanotime();
|
||||||
|
int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, NULL, NULL, &tm));
|
||||||
|
if (ret == -1) {
|
||||||
|
printf("*** %s: select failed: %s\n", path, strerror(errno));
|
||||||
|
newline = true;
|
||||||
|
break;
|
||||||
|
} else if (ret == 0) {
|
||||||
|
elapsed = nanotime() - elapsed;
|
||||||
|
printf("*** %s: Timed out after %.3fs\n", path,
|
||||||
|
(float) elapsed / NANOS_PER_SEC);
|
||||||
|
newline = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
char buffer[65536];
|
||||||
|
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
fwrite(buffer, bytes_read, 1, stdout);
|
||||||
|
newline = (buffer[bytes_read-1] == '\n');
|
||||||
|
} else {
|
||||||
|
if (bytes_read == -1) {
|
||||||
|
printf("*** %s: Failed to read from fd: %s", path, strerror(errno));
|
||||||
|
newline = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ret <= 0) break;
|
|
||||||
}
|
}
|
||||||
close(fd);
|
TEMP_FAILURE_RETRY(close(fd));
|
||||||
|
|
||||||
if (!newline) printf("\n");
|
if (!newline) printf("\n");
|
||||||
if (title) printf("\n");
|
if (title) printf("\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int64_t nanotime() {
|
/* prints the contents of a file */
|
||||||
struct timespec ts;
|
int dump_file(const char *title, const char *path) {
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC));
|
||||||
return (int64_t)ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec;
|
if (fd < 0) {
|
||||||
|
int err = errno;
|
||||||
|
if (title) printf("------ %s (%s) ------\n", title, path);
|
||||||
|
printf("*** %s: %s\n", path, strerror(err));
|
||||||
|
if (title) printf("\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return _dump_file_from_fd(title, path, fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* fd must have been opened with the flag O_NONBLOCK. With this flag set,
|
||||||
|
* it's possible to avoid issues where opening the file itself can get
|
||||||
|
* stuck.
|
||||||
|
*/
|
||||||
|
int dump_file_from_fd(const char *title, const char *path, int fd) {
|
||||||
|
int flags = fcntl(fd, F_GETFL);
|
||||||
|
if (flags == -1) {
|
||||||
|
printf("*** %s: failed to get flags on fd %d: %s\n", path, fd, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
} else if (!(flags & O_NONBLOCK)) {
|
||||||
|
printf("*** %s: fd must have O_NONBLOCK set.\n", path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return _dump_file_from_fd(title, path, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
|
bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
|
||||||
@ -348,7 +389,7 @@ bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
|
|||||||
/* forks a command and waits for it to finish */
|
/* forks a command and waits for it to finish */
|
||||||
int run_command(const char *title, int timeout_seconds, const char *command, ...) {
|
int run_command(const char *title, int timeout_seconds, const char *command, ...) {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
int64_t start = nanotime();
|
uint64_t start = nanotime();
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
/* handle error case */
|
/* handle error case */
|
||||||
@ -550,7 +591,8 @@ const char *dump_traces() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* create a new, empty traces.txt file to receive stack dumps */
|
/* create a new, empty traces.txt file to receive stack dumps */
|
||||||
int fd = open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW, 0666); /* -rw-rw-rw- */
|
int fd = TEMP_FAILURE_RETRY(open(traces_path, O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW,
|
||||||
|
0666)); /* -rw-rw-rw- */
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
|
fprintf(stderr, "%s: %s\n", traces_path, strerror(errno));
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -600,7 +642,7 @@ const char *dump_traces() {
|
|||||||
if (!strncmp(data, "/system/bin/app_process", strlen("/system/bin/app_process"))) {
|
if (!strncmp(data, "/system/bin/app_process", strlen("/system/bin/app_process"))) {
|
||||||
/* skip zygote -- it won't dump its stack anyway */
|
/* skip zygote -- it won't dump its stack anyway */
|
||||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||||
int cfd = open(path, O_RDONLY);
|
int cfd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));
|
||||||
len = read(cfd, data, sizeof(data) - 1);
|
len = read(cfd, data, sizeof(data) - 1);
|
||||||
close(cfd);
|
close(cfd);
|
||||||
if (len <= 0) {
|
if (len <= 0) {
|
||||||
@ -612,7 +654,7 @@ const char *dump_traces() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
++dalvik_found;
|
++dalvik_found;
|
||||||
int64_t start = nanotime();
|
uint64_t start = nanotime();
|
||||||
if (kill(pid, SIGQUIT)) {
|
if (kill(pid, SIGQUIT)) {
|
||||||
fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
|
fprintf(stderr, "kill(%d, SIGQUIT): %s\n", pid, strerror(errno));
|
||||||
continue;
|
continue;
|
||||||
@ -642,7 +684,7 @@ const char *dump_traces() {
|
|||||||
fprintf(stderr, "lseek: %s\n", strerror(errno));
|
fprintf(stderr, "lseek: %s\n", strerror(errno));
|
||||||
} else {
|
} else {
|
||||||
static uint16_t timeout_failures = 0;
|
static uint16_t timeout_failures = 0;
|
||||||
int64_t start = nanotime();
|
uint64_t start = nanotime();
|
||||||
|
|
||||||
/* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
|
/* If 3 backtrace dumps fail in a row, consider debuggerd dead. */
|
||||||
if (timeout_failures == 3) {
|
if (timeout_failures == 3) {
|
||||||
|
Loading…
Reference in New Issue
Block a user