1 /* 2 * Copyright (C) 2012 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 <stddef.h> 18 #include <stdbool.h> 19 #include <stdlib.h> 20 #include <string.h> 21 #include <stdio.h> 22 #include <time.h> 23 #include <errno.h> 24 #include <limits.h> 25 #include <dirent.h> 26 #include <unistd.h> 27 #include <sys/types.h> 28 #include <sys/ptrace.h> 29 30 #include <corkscrew/backtrace.h> 31 32 #include "tombstone.h" 33 #include "utility.h" 34 35 #define STACK_DEPTH 32 36 37 static void dump_process_header(log_t* log, pid_t pid) { 38 char path[PATH_MAX]; 39 char procnamebuf[1024]; 40 char* procname = NULL; 41 FILE* fp; 42 43 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); 44 if ((fp = fopen(path, "r"))) { 45 procname = fgets(procnamebuf, sizeof(procnamebuf), fp); 46 fclose(fp); 47 } 48 49 time_t t = time(NULL); 50 struct tm tm; 51 localtime_r(&t, &tm); 52 char timestr[64]; 53 strftime(timestr, sizeof(timestr), "%F %T", &tm); 54 _LOG(log, SCOPE_AT_FAULT, "\n\n----- pid %d at %s -----\n", pid, timestr); 55 56 if (procname) { 57 _LOG(log, SCOPE_AT_FAULT, "Cmd line: %s\n", procname); 58 } 59 } 60 61 static void dump_process_footer(log_t* log, pid_t pid) { 62 _LOG(log, SCOPE_AT_FAULT, "\n----- end %d -----\n", pid); 63 } 64 65 static void dump_thread(log_t* log, pid_t tid, ptrace_context_t* context, bool attached, 66 bool* detach_failed, int* total_sleep_time_usec) { 67 char path[PATH_MAX]; 68 char threadnamebuf[1024]; 69 char* threadname = NULL; 70 FILE* fp; 71 72 snprintf(path, sizeof(path), "/proc/%d/comm", tid); 73 if ((fp = fopen(path, "r"))) { 74 threadname = fgets(threadnamebuf, sizeof(threadnamebuf), fp); 75 fclose(fp); 76 if (threadname) { 77 size_t len = strlen(threadname); 78 if (len && threadname[len - 1] == '\n') { 79 threadname[len - 1] = '\0'; 80 } 81 } 82 } 83 84 _LOG(log, SCOPE_AT_FAULT, "\n\"%s\" sysTid=%d\n", 85 threadname ? threadname : "<unknown>", tid); 86 87 if (!attached && ptrace(PTRACE_ATTACH, tid, 0, 0) < 0) { 88 _LOG(log, SCOPE_AT_FAULT, "Could not attach to thread: %s\n", strerror(errno)); 89 return; 90 } 91 92 wait_for_stop(tid, total_sleep_time_usec); 93 94 backtrace_frame_t backtrace[STACK_DEPTH]; 95 ssize_t frames = unwind_backtrace_ptrace(tid, context, backtrace, 0, STACK_DEPTH); 96 if (frames <= 0) { 97 _LOG(log, SCOPE_AT_FAULT, "Could not obtain stack trace for thread.\n"); 98 } else { 99 backtrace_symbol_t backtrace_symbols[STACK_DEPTH]; 100 get_backtrace_symbols_ptrace(context, backtrace, frames, backtrace_symbols); 101 for (size_t i = 0; i < (size_t)frames; i++) { 102 char line[MAX_BACKTRACE_LINE_LENGTH]; 103 format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i], 104 line, MAX_BACKTRACE_LINE_LENGTH); 105 _LOG(log, SCOPE_AT_FAULT, " %s\n", line); 106 } 107 free_backtrace_symbols(backtrace_symbols, frames); 108 } 109 110 if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) { 111 LOG("ptrace detach from %d failed: %s\n", tid, strerror(errno)); 112 *detach_failed = true; 113 } 114 } 115 116 void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed, 117 int* total_sleep_time_usec) { 118 log_t log; 119 log.tfd = fd; 120 log.amfd = amfd; 121 log.quiet = true; 122 123 ptrace_context_t* context = load_ptrace_context(tid); 124 dump_process_header(&log, pid); 125 dump_thread(&log, tid, context, true, detach_failed, total_sleep_time_usec); 126 127 char task_path[64]; 128 snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid); 129 DIR* d = opendir(task_path); 130 if (d != NULL) { 131 struct dirent* de = NULL; 132 while ((de = readdir(d)) != NULL) { 133 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { 134 continue; 135 } 136 137 char* end; 138 pid_t new_tid = strtoul(de->d_name, &end, 10); 139 if (*end || new_tid == tid) { 140 continue; 141 } 142 143 dump_thread(&log, new_tid, context, false, detach_failed, total_sleep_time_usec); 144 } 145 closedir(d); 146 } 147 148 dump_process_footer(&log, pid); 149 free_ptrace_context(context); 150 } 151