| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 1 | /* system/debuggerd/debuggerd.c | 
 | 2 | ** | 
 | 3 | ** Copyright 2006, The Android Open Source Project | 
 | 4 | ** | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 5 | ** Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 6 | ** you may not use this file except in compliance with the License. | 
 | 7 | ** You may obtain a copy of the License at | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 8 | ** | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 9 | **     http://www.apache.org/licenses/LICENSE-2.0 | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 10 | ** | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 11 | ** Unless required by applicable law or agreed to in writing, software | 
 | 12 | ** distributed under the License is distributed on an "AS IS" BASIS, | 
 | 13 | ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 14 | ** See the License for the specific language governing permissions and | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 15 | ** limitations under the License. | 
 | 16 | */ | 
 | 17 |  | 
 | 18 | #include <stdio.h> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 19 | #include <errno.h> | 
 | 20 | #include <signal.h> | 
 | 21 | #include <pthread.h> | 
 | 22 | #include <stdarg.h> | 
 | 23 | #include <fcntl.h> | 
 | 24 | #include <sys/types.h> | 
 | 25 | #include <dirent.h> | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 26 | #include <time.h> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 27 |  | 
 | 28 | #include <sys/ptrace.h> | 
 | 29 | #include <sys/wait.h> | 
 | 30 | #include <sys/exec_elf.h> | 
 | 31 | #include <sys/stat.h> | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 32 | #include <sys/poll.h> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 33 |  | 
 | 34 | #include <cutils/sockets.h> | 
 | 35 | #include <cutils/logd.h> | 
| Andy McFadden | 41e0cef | 2011-10-13 16:05:08 -0700 | [diff] [blame] | 36 | #include <cutils/logger.h> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 37 | #include <cutils/properties.h> | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 38 | #include <cutils/debugger.h> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 39 |  | 
| Jeff Brown | 13e715b | 2011-10-21 12:14:56 -0700 | [diff] [blame] | 40 | #include <corkscrew/backtrace.h> | 
 | 41 |  | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 42 | #include <linux/input.h> | 
 | 43 |  | 
 | 44 | #include <private/android_filesystem_config.h> | 
 | 45 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 46 | #include "backtrace.h" | 
| Jeff Brown | 13e715b | 2011-10-21 12:14:56 -0700 | [diff] [blame] | 47 | #include "getevent.h" | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 48 | #include "tombstone.h" | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 49 | #include "utility.h" | 
 | 50 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 51 | typedef struct { | 
 | 52 |     debugger_action_t action; | 
 | 53 |     pid_t pid, tid; | 
 | 54 |     uid_t uid, gid; | 
| Elliott Hughes | 707b8bb | 2013-04-04 13:52:01 -0700 | [diff] [blame] | 55 |     uintptr_t abort_msg_address; | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 56 | } debugger_request_t; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 57 |  | 
 | 58 | static int | 
 | 59 | write_string(const char* file, const char* string) | 
 | 60 | { | 
 | 61 |     int len; | 
 | 62 |     int fd; | 
 | 63 |     ssize_t amt; | 
 | 64 |     fd = open(file, O_RDWR); | 
 | 65 |     len = strlen(string); | 
 | 66 |     if (fd < 0) | 
 | 67 |         return -errno; | 
 | 68 |     amt = write(fd, string, len); | 
 | 69 |     close(fd); | 
 | 70 |     return amt >= 0 ? 0 : -errno; | 
 | 71 | } | 
 | 72 |  | 
 | 73 | static | 
 | 74 | void init_debug_led(void) | 
 | 75 | { | 
 | 76 |     // trout leds | 
 | 77 |     write_string("/sys/class/leds/red/brightness", "0"); | 
 | 78 |     write_string("/sys/class/leds/green/brightness", "0"); | 
 | 79 |     write_string("/sys/class/leds/blue/brightness", "0"); | 
 | 80 |     write_string("/sys/class/leds/red/device/blink", "0"); | 
 | 81 |     // sardine leds | 
 | 82 |     write_string("/sys/class/leds/left/cadence", "0,0"); | 
 | 83 | } | 
 | 84 |  | 
 | 85 | static | 
 | 86 | void enable_debug_led(void) | 
 | 87 | { | 
 | 88 |     // trout leds | 
 | 89 |     write_string("/sys/class/leds/red/brightness", "255"); | 
 | 90 |     // sardine leds | 
 | 91 |     write_string("/sys/class/leds/left/cadence", "1,0"); | 
 | 92 | } | 
 | 93 |  | 
 | 94 | static | 
 | 95 | void disable_debug_led(void) | 
 | 96 | { | 
 | 97 |     // trout leds | 
 | 98 |     write_string("/sys/class/leds/red/brightness", "0"); | 
 | 99 |     // sardine leds | 
 | 100 |     write_string("/sys/class/leds/left/cadence", "0,0"); | 
 | 101 | } | 
 | 102 |  | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 103 | static void wait_for_user_action(pid_t pid) { | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 104 |     /* First log a helpful message */ | 
 | 105 |     LOG(    "********************************************************\n" | 
| Andy McFadden | 3bfdcc9 | 2009-12-01 12:37:26 -0800 | [diff] [blame] | 106 |             "* Process %d has been suspended while crashing.  To\n" | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 107 |             "* attach gdbserver for a gdb connection on port 5039\n" | 
 | 108 |             "* and start gdbclient:\n" | 
| Andy McFadden | 3bfdcc9 | 2009-12-01 12:37:26 -0800 | [diff] [blame] | 109 |             "*\n" | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 110 |             "*     gdbclient app_process :5039 %d\n" | 
| Andy McFadden | 3bfdcc9 | 2009-12-01 12:37:26 -0800 | [diff] [blame] | 111 |             "*\n" | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 112 |             "* Wait for gdb to start, then press HOME or VOLUME DOWN key\n" | 
 | 113 |             "* to let the process continue crashing.\n" | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 114 |             "********************************************************\n", | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 115 |             pid, pid); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 116 |  | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 117 |     /* wait for HOME or VOLUME DOWN key */ | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 118 |     if (init_getevent() == 0) { | 
 | 119 |         int ms = 1200 / 10; | 
 | 120 |         int dit = 1; | 
 | 121 |         int dah = 3*dit; | 
 | 122 |         int _       = -dit; | 
 | 123 |         int ___     = 3*_; | 
 | 124 |         int _______ = 7*_; | 
 | 125 |         const signed char codes[] = { | 
 | 126 |            dit,_,dit,_,dit,___,dah,_,dah,_,dah,___,dit,_,dit,_,dit,_______ | 
 | 127 |         }; | 
 | 128 |         size_t s = 0; | 
 | 129 |         struct input_event e; | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 130 |         bool done = false; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 131 |         init_debug_led(); | 
 | 132 |         enable_debug_led(); | 
 | 133 |         do { | 
 | 134 |             int timeout = abs((int)(codes[s])) * ms; | 
 | 135 |             int res = get_event(&e, timeout); | 
 | 136 |             if (res == 0) { | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 137 |                 if (e.type == EV_KEY | 
 | 138 |                         && (e.code == KEY_HOME || e.code == KEY_VOLUMEDOWN) | 
 | 139 |                         && e.value == 0) { | 
 | 140 |                     done = true; | 
 | 141 |                 } | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 142 |             } else if (res == 1) { | 
 | 143 |                 if (++s >= sizeof(codes)/sizeof(*codes)) | 
 | 144 |                     s = 0; | 
 | 145 |                 if (codes[s] > 0) { | 
 | 146 |                     enable_debug_led(); | 
 | 147 |                 } else { | 
 | 148 |                     disable_debug_led(); | 
 | 149 |                 } | 
 | 150 |             } | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 151 |         } while (!done); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 152 |         uninit_getevent(); | 
 | 153 |     } | 
 | 154 |  | 
 | 155 |     /* don't forget to turn debug led off */ | 
 | 156 |     disable_debug_led(); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 157 |     LOG("debuggerd resuming process %d", pid); | 
 | 158 | } | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 159 |  | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 160 | static int get_process_info(pid_t tid, pid_t* out_pid, uid_t* out_uid, uid_t* out_gid) { | 
 | 161 |     char path[64]; | 
 | 162 |     snprintf(path, sizeof(path), "/proc/%d/status", tid); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 163 |  | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 164 |     FILE* fp = fopen(path, "r"); | 
 | 165 |     if (!fp) { | 
 | 166 |         return -1; | 
 | 167 |     } | 
 | 168 |  | 
 | 169 |     int fields = 0; | 
 | 170 |     char line[1024]; | 
 | 171 |     while (fgets(line, sizeof(line), fp)) { | 
 | 172 |         size_t len = strlen(line); | 
 | 173 |         if (len > 6 && !memcmp(line, "Tgid:\t", 6)) { | 
 | 174 |             *out_pid = atoi(line + 6); | 
 | 175 |             fields |= 1; | 
 | 176 |         } else if (len > 5 && !memcmp(line, "Uid:\t", 5)) { | 
 | 177 |             *out_uid = atoi(line + 5); | 
 | 178 |             fields |= 2; | 
 | 179 |         } else if (len > 5 && !memcmp(line, "Gid:\t", 5)) { | 
 | 180 |             *out_gid = atoi(line + 5); | 
 | 181 |             fields |= 4; | 
 | 182 |         } | 
 | 183 |     } | 
 | 184 |     fclose(fp); | 
 | 185 |     return fields == 7 ? 0 : -1; | 
 | 186 | } | 
 | 187 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 188 | static int read_request(int fd, debugger_request_t* out_request) { | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 189 |     struct ucred cr; | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 190 |     int len = sizeof(cr); | 
 | 191 |     int status = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &len); | 
 | 192 |     if (status != 0) { | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 193 |         LOG("cannot get credentials\n"); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 194 |         return -1; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 195 |     } | 
 | 196 |  | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 197 |     XLOG("reading tid\n"); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 198 |     fcntl(fd, F_SETFL, O_NONBLOCK); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 199 |  | 
 | 200 |     struct pollfd pollfds[1]; | 
 | 201 |     pollfds[0].fd = fd; | 
 | 202 |     pollfds[0].events = POLLIN; | 
 | 203 |     pollfds[0].revents = 0; | 
 | 204 |     status = TEMP_FAILURE_RETRY(poll(pollfds, 1, 3000)); | 
 | 205 |     if (status != 1) { | 
 | 206 |         LOG("timed out reading tid\n"); | 
 | 207 |         return -1; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 208 |     } | 
 | 209 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 210 |     debugger_msg_t msg; | 
| Elliott Hughes | 707b8bb | 2013-04-04 13:52:01 -0700 | [diff] [blame] | 211 |     memset(&msg, 0, sizeof(msg)); | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 212 |     status = TEMP_FAILURE_RETRY(read(fd, &msg, sizeof(msg))); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 213 |     if (status < 0) { | 
 | 214 |         LOG("read failure? %s\n", strerror(errno)); | 
 | 215 |         return -1; | 
 | 216 |     } | 
| Elliott Hughes | 707b8bb | 2013-04-04 13:52:01 -0700 | [diff] [blame] | 217 |     if (status == sizeof(debugger_msg_t)) { | 
 | 218 |         XLOG("crash request of size %d abort_msg_address=%#08x\n", status, msg.abort_msg_address); | 
 | 219 |     } else { | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 220 |         LOG("invalid crash request of size %d\n", status); | 
 | 221 |         return -1; | 
 | 222 |     } | 
 | 223 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 224 |     out_request->action = msg.action; | 
 | 225 |     out_request->tid = msg.tid; | 
 | 226 |     out_request->pid = cr.pid; | 
 | 227 |     out_request->uid = cr.uid; | 
 | 228 |     out_request->gid = cr.gid; | 
| Elliott Hughes | 707b8bb | 2013-04-04 13:52:01 -0700 | [diff] [blame] | 229 |     out_request->abort_msg_address = msg.abort_msg_address; | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 230 |  | 
 | 231 |     if (msg.action == DEBUGGER_ACTION_CRASH) { | 
 | 232 |         /* Ensure that the tid reported by the crashing process is valid. */ | 
 | 233 |         char buf[64]; | 
 | 234 |         struct stat s; | 
 | 235 |         snprintf(buf, sizeof buf, "/proc/%d/task/%d", out_request->pid, out_request->tid); | 
 | 236 |         if(stat(buf, &s)) { | 
 | 237 |             LOG("tid %d does not exist in pid %d. ignoring debug request\n", | 
 | 238 |                     out_request->tid, out_request->pid); | 
 | 239 |             return -1; | 
 | 240 |         } | 
 | 241 |     } else if (cr.uid == 0 | 
 | 242 |             || (cr.uid == AID_SYSTEM && msg.action == DEBUGGER_ACTION_DUMP_BACKTRACE)) { | 
 | 243 |         /* Only root or system can ask us to attach to any process and dump it explicitly. | 
 | 244 |          * However, system is only allowed to collect backtraces but cannot dump tombstones. */ | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 245 |         status = get_process_info(out_request->tid, &out_request->pid, | 
 | 246 |                 &out_request->uid, &out_request->gid); | 
 | 247 |         if (status < 0) { | 
 | 248 |             LOG("tid %d does not exist. ignoring explicit dump request\n", | 
 | 249 |                     out_request->tid); | 
 | 250 |             return -1; | 
 | 251 |         } | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 252 |     } else { | 
 | 253 |         /* No one else is not allowed to dump arbitrary processes. */ | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 254 |         return -1; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 255 |     } | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 256 |     return 0; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 257 | } | 
 | 258 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 259 | static bool should_attach_gdb(debugger_request_t* request) { | 
 | 260 |     if (request->action == DEBUGGER_ACTION_CRASH) { | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 261 |         char value[PROPERTY_VALUE_MAX]; | 
 | 262 |         property_get("debug.db.uid", value, "-1"); | 
 | 263 |         int debug_uid = atoi(value); | 
 | 264 |         return debug_uid >= 0 && request->uid <= (uid_t)debug_uid; | 
 | 265 |     } | 
 | 266 |     return false; | 
 | 267 | } | 
| Bruce Beare | 8492490 | 2010-10-13 14:21:30 -0700 | [diff] [blame] | 268 |  | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 269 | static void handle_request(int fd) { | 
 | 270 |     XLOG("handle_request(%d)\n", fd); | 
 | 271 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 272 |     debugger_request_t request; | 
| Elliott Hughes | 707b8bb | 2013-04-04 13:52:01 -0700 | [diff] [blame] | 273 |     memset(&request, 0, sizeof(request)); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 274 |     int status = read_request(fd, &request); | 
 | 275 |     if (!status) { | 
| Andy McFadden | 424e07f | 2012-03-08 15:27:49 -0800 | [diff] [blame] | 276 |         XLOG("BOOM: pid=%d uid=%d gid=%d tid=%d\n", | 
 | 277 |             request.pid, request.uid, request.gid, request.tid); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 278 |  | 
 | 279 |         /* At this point, the thread that made the request is blocked in | 
 | 280 |          * a read() call.  If the thread has crashed, then this gives us | 
 | 281 |          * time to PTRACE_ATTACH to it before it has a chance to really fault. | 
 | 282 |          * | 
 | 283 |          * The PTRACE_ATTACH sends a SIGSTOP to the target process, but it | 
 | 284 |          * won't necessarily have stopped by the time ptrace() returns.  (We | 
 | 285 |          * currently assume it does.)  We write to the file descriptor to | 
 | 286 |          * ensure that it can run as soon as we call PTRACE_CONT below. | 
 | 287 |          * See details in bionic/libc/linker/debugger.c, in function | 
 | 288 |          * debugger_signal_handler(). | 
 | 289 |          */ | 
 | 290 |         if (ptrace(PTRACE_ATTACH, request.tid, 0, 0)) { | 
 | 291 |             LOG("ptrace attach failed: %s\n", strerror(errno)); | 
 | 292 |         } else { | 
 | 293 |             bool detach_failed = false; | 
 | 294 |             bool attach_gdb = should_attach_gdb(&request); | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 295 |             if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 296 |                 LOG("failed responding to client: %s\n", strerror(errno)); | 
 | 297 |             } else { | 
| Jeff Brown | fb9804b | 2011-11-08 20:17:05 -0800 | [diff] [blame] | 298 |                 char* tombstone_path = NULL; | 
 | 299 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 300 |                 if (request.action == DEBUGGER_ACTION_CRASH) { | 
| Jeff Brown | fb9804b | 2011-11-08 20:17:05 -0800 | [diff] [blame] | 301 |                     close(fd); | 
 | 302 |                     fd = -1; | 
 | 303 |                 } | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 304 |  | 
 | 305 |                 int total_sleep_time_usec = 0; | 
 | 306 |                 for (;;) { | 
 | 307 |                     int signal = wait_for_signal(request.tid, &total_sleep_time_usec); | 
 | 308 |                     if (signal < 0) { | 
 | 309 |                         break; | 
 | 310 |                     } | 
 | 311 |  | 
 | 312 |                     switch (signal) { | 
 | 313 |                     case SIGSTOP: | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 314 |                         if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { | 
 | 315 |                             XLOG("stopped -- dumping to tombstone\n"); | 
| Jeff Brown | fb9804b | 2011-11-08 20:17:05 -0800 | [diff] [blame] | 316 |                             tombstone_path = engrave_tombstone(request.pid, request.tid, | 
| Elliott Hughes | 707b8bb | 2013-04-04 13:52:01 -0700 | [diff] [blame] | 317 |                                     signal, request.abort_msg_address, true, true, &detach_failed, | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 318 |                                     &total_sleep_time_usec); | 
 | 319 |                         } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { | 
 | 320 |                             XLOG("stopped -- dumping to fd\n"); | 
 | 321 |                             dump_backtrace(fd, request.pid, request.tid, &detach_failed, | 
 | 322 |                                     &total_sleep_time_usec); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 323 |                         } else { | 
 | 324 |                             XLOG("stopped -- continuing\n"); | 
 | 325 |                             status = ptrace(PTRACE_CONT, request.tid, 0, 0); | 
 | 326 |                             if (status) { | 
 | 327 |                                 LOG("ptrace continue failed: %s\n", strerror(errno)); | 
 | 328 |                             } | 
 | 329 |                             continue; /* loop again */ | 
 | 330 |                         } | 
 | 331 |                         break; | 
 | 332 |  | 
 | 333 |                     case SIGILL: | 
 | 334 |                     case SIGABRT: | 
 | 335 |                     case SIGBUS: | 
 | 336 |                     case SIGFPE: | 
 | 337 |                     case SIGSEGV: | 
| Andy McFadden | 424e07f | 2012-03-08 15:27:49 -0800 | [diff] [blame] | 338 |                     case SIGPIPE: | 
| Chris Dearman | 231e3c8 | 2012-08-10 17:06:20 -0700 | [diff] [blame] | 339 | #ifdef SIGSTKFLT | 
 | 340 |                     case SIGSTKFLT: | 
 | 341 | #endif | 
 | 342 |                         { | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 343 |                         XLOG("stopped -- fatal signal\n"); | 
| Andy McFadden | 424e07f | 2012-03-08 15:27:49 -0800 | [diff] [blame] | 344 |                         /* | 
 | 345 |                          * Send a SIGSTOP to the process to make all of | 
 | 346 |                          * the non-signaled threads stop moving.  Without | 
 | 347 |                          * this we get a lot of "ptrace detach failed: | 
 | 348 |                          * No such process". | 
 | 349 |                          */ | 
 | 350 |                         kill(request.pid, SIGSTOP); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 351 |                         /* don't dump sibling threads when attaching to GDB because it | 
 | 352 |                          * makes the process less reliable, apparently... */ | 
| Jeff Brown | fb9804b | 2011-11-08 20:17:05 -0800 | [diff] [blame] | 353 |                         tombstone_path = engrave_tombstone(request.pid, request.tid, | 
| Elliott Hughes | 707b8bb | 2013-04-04 13:52:01 -0700 | [diff] [blame] | 354 |                                 signal, request.abort_msg_address, !attach_gdb, false, | 
 | 355 |                                 &detach_failed, &total_sleep_time_usec); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 356 |                         break; | 
 | 357 |                     } | 
 | 358 |  | 
 | 359 |                     default: | 
 | 360 |                         XLOG("stopped -- unexpected signal\n"); | 
 | 361 |                         LOG("process stopped due to unexpected signal %d\n", signal); | 
 | 362 |                         break; | 
 | 363 |                     } | 
 | 364 |                     break; | 
 | 365 |                 } | 
| Jeff Brown | fb9804b | 2011-11-08 20:17:05 -0800 | [diff] [blame] | 366 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 367 |                 if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { | 
| Jeff Brown | fb9804b | 2011-11-08 20:17:05 -0800 | [diff] [blame] | 368 |                     if (tombstone_path) { | 
 | 369 |                         write(fd, tombstone_path, strlen(tombstone_path)); | 
 | 370 |                     } | 
 | 371 |                     close(fd); | 
 | 372 |                     fd = -1; | 
 | 373 |                 } | 
 | 374 |                 free(tombstone_path); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 375 |             } | 
 | 376 |  | 
 | 377 |             XLOG("detaching\n"); | 
 | 378 |             if (attach_gdb) { | 
 | 379 |                 /* stop the process so we can debug */ | 
 | 380 |                 kill(request.pid, SIGSTOP); | 
 | 381 |  | 
 | 382 |                 /* detach so we can attach gdbserver */ | 
 | 383 |                 if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { | 
 | 384 |                     LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); | 
 | 385 |                     detach_failed = true; | 
 | 386 |                 } | 
 | 387 |  | 
 | 388 |                 /* | 
 | 389 |                  * if debug.db.uid is set, its value indicates if we should wait | 
 | 390 |                  * for user action for the crashing process. | 
 | 391 |                  * in this case, we log a message and turn the debug LED on | 
 | 392 |                  * waiting for a gdb connection (for instance) | 
 | 393 |                  */ | 
 | 394 |                 wait_for_user_action(request.pid); | 
 | 395 |             } else { | 
 | 396 |                 /* just detach */ | 
 | 397 |                 if (ptrace(PTRACE_DETACH, request.tid, 0, 0)) { | 
 | 398 |                     LOG("ptrace detach from %d failed: %s\n", request.tid, strerror(errno)); | 
 | 399 |                     detach_failed = true; | 
 | 400 |                 } | 
 | 401 |             } | 
 | 402 |  | 
 | 403 |             /* resume stopped process (so it can crash in peace). */ | 
 | 404 |             kill(request.pid, SIGCONT); | 
 | 405 |  | 
 | 406 |             /* If we didn't successfully detach, we're still the parent, and the | 
 | 407 |              * actual parent won't receive a death notification via wait(2).  At this point | 
 | 408 |              * there's not much we can do about that. */ | 
 | 409 |             if (detach_failed) { | 
 | 410 |                 LOG("debuggerd committing suicide to free the zombie!\n"); | 
 | 411 |                 kill(getpid(), SIGKILL); | 
 | 412 |             } | 
 | 413 |         } | 
 | 414 |  | 
 | 415 |     } | 
 | 416 |     if (fd >= 0) { | 
 | 417 |         close(fd); | 
 | 418 |     } | 
 | 419 | } | 
 | 420 |  | 
 | 421 | static int do_server() { | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 422 |     int s; | 
 | 423 |     struct sigaction act; | 
| Bruce Beare | 8492490 | 2010-10-13 14:21:30 -0700 | [diff] [blame] | 424 |     int logsocket = -1; | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 425 |  | 
| Andy McFadden | 44e12ec | 2011-07-29 12:36:47 -0700 | [diff] [blame] | 426 |     /* | 
 | 427 |      * debuggerd crashes can't be reported to debuggerd.  Reset all of the | 
 | 428 |      * crash handlers. | 
 | 429 |      */ | 
 | 430 |     signal(SIGILL, SIG_DFL); | 
 | 431 |     signal(SIGABRT, SIG_DFL); | 
 | 432 |     signal(SIGBUS, SIG_DFL); | 
 | 433 |     signal(SIGFPE, SIG_DFL); | 
 | 434 |     signal(SIGSEGV, SIG_DFL); | 
| Andy McFadden | 44e12ec | 2011-07-29 12:36:47 -0700 | [diff] [blame] | 435 |     signal(SIGPIPE, SIG_DFL); | 
| Chris Dearman | 231e3c8 | 2012-08-10 17:06:20 -0700 | [diff] [blame] | 436 | #ifdef SIGSTKFLT | 
| Andy McFadden | 424e07f | 2012-03-08 15:27:49 -0800 | [diff] [blame] | 437 |     signal(SIGSTKFLT, SIG_DFL); | 
| Chris Dearman | 231e3c8 | 2012-08-10 17:06:20 -0700 | [diff] [blame] | 438 | #endif | 
| Andy McFadden | 44e12ec | 2011-07-29 12:36:47 -0700 | [diff] [blame] | 439 |  | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 440 |     logsocket = socket_local_client("logd", | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 441 |             ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_DGRAM); | 
 | 442 |     if(logsocket < 0) { | 
 | 443 |         logsocket = -1; | 
 | 444 |     } else { | 
 | 445 |         fcntl(logsocket, F_SETFD, FD_CLOEXEC); | 
 | 446 |     } | 
 | 447 |  | 
 | 448 |     act.sa_handler = SIG_DFL; | 
 | 449 |     sigemptyset(&act.sa_mask); | 
 | 450 |     sigaddset(&act.sa_mask,SIGCHLD); | 
 | 451 |     act.sa_flags = SA_NOCLDWAIT; | 
 | 452 |     sigaction(SIGCHLD, &act, 0); | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 453 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 454 |     s = socket_local_server(DEBUGGER_SOCKET_NAME, | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 455 |             ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 456 |     if(s < 0) return 1; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 457 |     fcntl(s, F_SETFD, FD_CLOEXEC); | 
 | 458 |  | 
 | 459 |     LOG("debuggerd: " __DATE__ " " __TIME__ "\n"); | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 460 |  | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 461 |     for(;;) { | 
 | 462 |         struct sockaddr addr; | 
 | 463 |         socklen_t alen; | 
 | 464 |         int fd; | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 465 |  | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 466 |         alen = sizeof(addr); | 
| Andy McFadden | 655835b | 2011-07-26 07:50:37 -0700 | [diff] [blame] | 467 |         XLOG("waiting for connection\n"); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 468 |         fd = accept(s, &addr, &alen); | 
| Andy McFadden | 655835b | 2011-07-26 07:50:37 -0700 | [diff] [blame] | 469 |         if(fd < 0) { | 
 | 470 |             XLOG("accept failed: %s\n", strerror(errno)); | 
 | 471 |             continue; | 
 | 472 |         } | 
| Ben Cheng | 09e7137 | 2009-09-28 11:06:09 -0700 | [diff] [blame] | 473 |  | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 474 |         fcntl(fd, F_SETFD, FD_CLOEXEC); | 
 | 475 |  | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 476 |         handle_request(fd); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 477 |     } | 
 | 478 |     return 0; | 
 | 479 | } | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 480 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 481 | static int do_explicit_dump(pid_t tid, bool dump_backtrace) { | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 482 |     fprintf(stdout, "Sending request to dump task %d.\n", tid); | 
 | 483 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 484 |     if (dump_backtrace) { | 
 | 485 |         fflush(stdout); | 
 | 486 |         if (dump_backtrace_to_file(tid, fileno(stdout)) < 0) { | 
 | 487 |             fputs("Error dumping backtrace.\n", stderr); | 
 | 488 |             return 1; | 
 | 489 |         } | 
| Jeff Brown | fb9804b | 2011-11-08 20:17:05 -0800 | [diff] [blame] | 490 |     } else { | 
 | 491 |         char tombstone_path[PATH_MAX]; | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 492 |         if (dump_tombstone(tid, tombstone_path, sizeof(tombstone_path)) < 0) { | 
 | 493 |             fputs("Error dumping tombstone.\n", stderr); | 
 | 494 |             return 1; | 
| Jeff Brown | fb9804b | 2011-11-08 20:17:05 -0800 | [diff] [blame] | 495 |         } | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 496 |         fprintf(stderr, "Tombstone written to: %s\n", tombstone_path); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 497 |     } | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 498 |     return 0; | 
 | 499 | } | 
 | 500 |  | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 501 | static void usage() { | 
 | 502 |     fputs("Usage: -b [<tid>]\n" | 
 | 503 |             "  -b dump backtrace to console, otherwise dump full tombstone file\n" | 
 | 504 |             "\n" | 
 | 505 |             "If tid specified, sends a request to debuggerd to dump that task.\n" | 
 | 506 |             "Otherwise, starts the debuggerd server.\n", stderr); | 
 | 507 | } | 
 | 508 |  | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 509 | int main(int argc, char** argv) { | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 510 |     if (argc == 1) { | 
 | 511 |         return do_server(); | 
 | 512 |     } | 
 | 513 |  | 
 | 514 |     bool dump_backtrace = false; | 
 | 515 |     bool have_tid = false; | 
 | 516 |     pid_t tid = 0; | 
 | 517 |     for (int i = 1; i < argc; i++) { | 
 | 518 |         if (!strcmp(argv[i], "-b")) { | 
 | 519 |             dump_backtrace = true; | 
 | 520 |         } else if (!have_tid) { | 
 | 521 |             tid = atoi(argv[i]); | 
 | 522 |             have_tid = true; | 
 | 523 |         } else { | 
 | 524 |             usage(); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 525 |             return 1; | 
 | 526 |         } | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 527 |     } | 
| Jeff Brown | 053b865 | 2012-06-06 16:25:03 -0700 | [diff] [blame] | 528 |     if (!have_tid) { | 
 | 529 |         usage(); | 
 | 530 |         return 1; | 
 | 531 |     } | 
 | 532 |     return do_explicit_dump(tid, dump_backtrace); | 
| Jeff Brown | 9524e41 | 2011-10-24 11:10:16 -0700 | [diff] [blame] | 533 | } |