| OLD | NEW |
| 1 // Copyright (c) 2009, Google Inc. | 1 // Copyright (c) 2009, Google Inc. |
| 2 // All rights reserved. | 2 // All rights reserved. |
| 3 // | 3 // |
| 4 // Redistribution and use in source and binary forms, with or without | 4 // Redistribution and use in source and binary forms, with or without |
| 5 // modification, are permitted provided that the following conditions are | 5 // modification, are permitted provided that the following conditions are |
| 6 // met: | 6 // met: |
| 7 // | 7 // |
| 8 // * Redistributions of source code must retain the above copyright | 8 // * Redistributions of source code must retain the above copyright |
| 9 // notice, this list of conditions and the following disclaimer. | 9 // notice, this list of conditions and the following disclaimer. |
| 10 // * Redistributions in binary form must reproduce the above | 10 // * Redistributions in binary form must reproduce the above |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 bool LinuxDumper::ThreadsResume() { | 103 bool LinuxDumper::ThreadsResume() { |
| 104 if (!threads_suspened_) | 104 if (!threads_suspened_) |
| 105 return false; | 105 return false; |
| 106 bool good = true; | 106 bool good = true; |
| 107 for (size_t i = 0; i < threads_.size(); ++i) | 107 for (size_t i = 0; i < threads_.size(); ++i) |
| 108 good &= ResumeThread(threads_[i]); | 108 good &= ResumeThread(threads_[i]); |
| 109 threads_suspened_ = false; | 109 threads_suspened_ = false; |
| 110 return good; | 110 return good; |
| 111 } | 111 } |
| 112 | 112 |
| 113 void |
| 114 LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { |
| 115 assert(path); |
| 116 if (!path) { |
| 117 return; |
| 118 } |
| 119 |
| 120 path[0] = '\0'; |
| 121 |
| 122 const unsigned pid_len = my_int_len(pid); |
| 123 |
| 124 assert(node); |
| 125 if (!node) { |
| 126 return; |
| 127 } |
| 128 |
| 129 size_t node_len = my_strlen(node); |
| 130 assert(node_len < NAME_MAX); |
| 131 if (node_len >= NAME_MAX) { |
| 132 return; |
| 133 } |
| 134 |
| 135 assert(node_len > 0); |
| 136 if (node_len == 0) { |
| 137 return; |
| 138 } |
| 139 |
| 140 assert(pid > 0); |
| 141 if (pid <= 0) { |
| 142 return; |
| 143 } |
| 144 |
| 145 const size_t total_length = 6 + pid_len + 1 + node_len; |
| 146 |
| 147 assert(total_length < NAME_MAX); |
| 148 if (total_length >= NAME_MAX) { |
| 149 return; |
| 150 } |
| 151 |
| 152 memcpy(path, "/proc/", 6); |
| 153 my_itos(path + 6, pid, pid_len); |
| 154 memcpy(path + 6 + pid_len, "/", 1); |
| 155 memcpy(path + 6 + pid_len + 1, node, node_len); |
| 156 memcpy(path + total_length, "\0", 1); |
| 157 } |
| 158 |
| 159 void* |
| 160 LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { |
| 161 char auxv_path[80]; |
| 162 BuildProcPath(auxv_path, pid, "auxv"); |
| 163 |
| 164 // If BuildProcPath errors out due to invalid input, we'll handle it when |
| 165 // we try to sys_open the file. |
| 166 |
| 167 // Find the AT_SYSINFO_EHDR entry for linux-gate.so |
| 168 // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more |
| 169 // information. |
| 170 int fd = sys_open(auxv_path, O_RDONLY, 0); |
| 171 if (fd < 0) { |
| 172 return NULL; |
| 173 } |
| 174 |
| 175 elf_aux_entry one_aux_entry; |
| 176 while (sys_read(fd, |
| 177 &one_aux_entry, |
| 178 sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && |
| 179 one_aux_entry.a_type != AT_NULL) { |
| 180 if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { |
| 181 close(fd); |
| 182 return reinterpret_cast<void*>(one_aux_entry.a_un.a_val); |
| 183 } |
| 184 } |
| 185 close(fd); |
| 186 return NULL; |
| 187 } |
| 188 |
| 113 bool | 189 bool |
| 114 LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*> *result) const { | 190 LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*>* result) const { |
| 115 char maps_path[80]; | 191 char maps_path[80]; |
| 116 memcpy(maps_path, "/proc/", 6); | 192 BuildProcPath(maps_path, pid_, "maps"); |
| 117 const unsigned pid_len = my_int_len(pid_); | 193 |
| 118 my_itos(maps_path + 6, pid_, pid_len); | 194 // linux_gate_loc is the beginning of the kernel's mapping of |
| 119 memcpy(maps_path + 6 + pid_len, "/maps", 6); | 195 // linux-gate.so in the process. It doesn't actually show up in the |
| 196 // maps list as a filename, so we use the aux vector to find it's |
| 197 // load location and special case it's entry when creating the list |
| 198 // of mappings. |
| 199 const void* linux_gate_loc; |
| 200 linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); |
| 120 | 201 |
| 121 const int fd = sys_open(maps_path, O_RDONLY, 0); | 202 const int fd = sys_open(maps_path, O_RDONLY, 0); |
| 122 if (fd < 0) | 203 if (fd < 0) |
| 123 return false; | 204 return false; |
| 124 LineReader *const line_reader = new(allocator_) LineReader(fd); | 205 LineReader* const line_reader = new(allocator_) LineReader(fd); |
| 125 | 206 |
| 126 const char *line; | 207 const char* line; |
| 127 unsigned line_len; | 208 unsigned line_len; |
| 128 while (line_reader->GetNextLine(&line, &line_len)) { | 209 while (line_reader->GetNextLine(&line, &line_len)) { |
| 129 uintptr_t start_addr, end_addr, offset; | 210 uintptr_t start_addr, end_addr, offset; |
| 130 | 211 |
| 131 const char* i1 = my_read_hex_ptr(&start_addr, line); | 212 const char* i1 = my_read_hex_ptr(&start_addr, line); |
| 132 if (*i1 == '-') { | 213 if (*i1 == '-') { |
| 133 const char *i2 = my_read_hex_ptr(&end_addr, i1 + 1); | 214 const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); |
| 134 if (*i2 == ' ') { | 215 if (*i2 == ' ') { |
| 135 const char *i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); | 216 const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); |
| 136 if (*i3 == ' ') { | 217 if (*i3 == ' ') { |
| 137 MappingInfo *const module = new(allocator_) MappingInfo; | 218 MappingInfo* const module = new(allocator_) MappingInfo; |
| 138 memset(module, 0, sizeof(MappingInfo)); | 219 memset(module, 0, sizeof(MappingInfo)); |
| 139 module->start_addr = start_addr; | 220 module->start_addr = start_addr; |
| 140 module->size = end_addr - start_addr; | 221 module->size = end_addr - start_addr; |
| 141 module->offset = offset; | 222 module->offset = offset; |
| 142 const char *name = NULL; | 223 const char* name = NULL; |
| 143 // Only copy name if the name is a valid path name. | 224 // Only copy name if the name is a valid path name, or if |
| 225 // we've found the VDSO image |
| 144 if ((name = my_strchr(line, '/')) != NULL) { | 226 if ((name = my_strchr(line, '/')) != NULL) { |
| 145 const unsigned l = my_strlen(name); | 227 const unsigned l = my_strlen(name); |
| 146 if (l < sizeof(module->name)) | 228 if (l < sizeof(module->name)) |
| 147 memcpy(module->name, name, l); | 229 memcpy(module->name, name, l); |
| 230 } else if (linux_gate_loc && |
| 231 reinterpret_cast<void*>(module->start_addr) == |
| 232 linux_gate_loc) { |
| 233 memcpy(module->name, |
| 234 kLinuxGateLibraryName, |
| 235 my_strlen(kLinuxGateLibraryName)); |
| 236 module->offset = 0; |
| 148 } | 237 } |
| 149 | |
| 150 result->push_back(module); | 238 result->push_back(module); |
| 151 } | 239 } |
| 152 } | 240 } |
| 153 } | 241 } |
| 154 | |
| 155 line_reader->PopLine(line_len); | 242 line_reader->PopLine(line_len); |
| 156 } | 243 } |
| 157 | 244 |
| 158 sys_close(fd); | 245 sys_close(fd); |
| 246 |
| 159 return result->size() > 0; | 247 return result->size() > 0; |
| 160 } | 248 } |
| 161 | 249 |
| 162 // Parse /proc/$pid/task to list all the threads of the process identified by | 250 // Parse /proc/$pid/task to list all the threads of the process identified by |
| 163 // pid. | 251 // pid. |
| 164 bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t> *result) const { | 252 bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const { |
| 165 char task_path[80]; | 253 char task_path[80]; |
| 166 memcpy(task_path, "/proc/", 6); | 254 BuildProcPath(task_path, pid_, "task"); |
| 167 const unsigned pid_len = my_int_len(pid_); | |
| 168 my_itos(task_path + 6, pid_, pid_len); | |
| 169 memcpy(task_path + 6 + pid_len, "/task", 6); | |
| 170 | 255 |
| 171 const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); | 256 const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); |
| 172 if (fd < 0) | 257 if (fd < 0) |
| 173 return false; | 258 return false; |
| 174 DirectoryReader *dir_reader = new(allocator_) DirectoryReader(fd); | 259 DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); |
| 175 | 260 |
| 176 // The directory may contain duplicate entries which we filter by assuming | 261 // The directory may contain duplicate entries which we filter by assuming |
| 177 // that they are consecutive | 262 // that they are consecutive. |
| 178 int last_tid = -1; | 263 int last_tid = -1; |
| 179 const char *dent_name; | 264 const char* dent_name; |
| 180 while (dir_reader->GetNextEntry(&dent_name)) { | 265 while (dir_reader->GetNextEntry(&dent_name)) { |
| 181 if (my_strcmp(dent_name, ".") && | 266 if (my_strcmp(dent_name, ".") && |
| 182 my_strcmp(dent_name, "..")) { | 267 my_strcmp(dent_name, "..")) { |
| 183 int tid = 0; | 268 int tid = 0; |
| 184 if (my_strtoui(&tid, dent_name) && | 269 if (my_strtoui(&tid, dent_name) && |
| 185 last_tid != tid) { | 270 last_tid != tid) { |
| 186 last_tid = tid; | 271 last_tid = tid; |
| 187 result->push_back(tid); | 272 result->push_back(tid); |
| 188 } | 273 } |
| 189 } | 274 } |
| 190 dir_reader->PopEntry(); | 275 dir_reader->PopEntry(); |
| 191 } | 276 } |
| 192 | 277 |
| 193 sys_close(fd); | 278 sys_close(fd); |
| 194 return true; | 279 return true; |
| 195 } | 280 } |
| 196 | 281 |
| 197 // Read thread info from /proc/$pid/status. | 282 // Read thread info from /proc/$pid/status. |
| 198 // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible, | 283 // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible, |
| 199 // these members are set to -1. Returns true iff all three members are | 284 // these members are set to -1. Returns true iff all three members are |
| 200 // availible. | 285 // availible. |
| 201 bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo *info) { | 286 bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { |
| 202 assert(info != NULL); | 287 assert(info != NULL); |
| 203 char status_path[80]; | 288 char status_path[80]; |
| 204 memcpy(status_path, "/proc/", 6); | 289 BuildProcPath(status_path, tid, "status"); |
| 205 const unsigned tid_len = my_int_len(tid); | 290 |
| 206 my_itos(status_path + 6, tid, tid_len); | |
| 207 memcpy(status_path + 6 + tid_len, "/status", 8); | |
| 208 const int fd = open(status_path, O_RDONLY); | 291 const int fd = open(status_path, O_RDONLY); |
| 209 if (fd < 0) | 292 if (fd < 0) |
| 210 return false; | 293 return false; |
| 211 | 294 |
| 212 LineReader *const line_reader = new(allocator_) LineReader(fd); | 295 LineReader* const line_reader = new(allocator_) LineReader(fd); |
| 213 const char *line; | 296 const char* line; |
| 214 unsigned line_len; | 297 unsigned line_len; |
| 215 | 298 |
| 216 info->ppid = info->tgid = -1; | 299 info->ppid = info->tgid = -1; |
| 217 | 300 |
| 218 while (line_reader->GetNextLine(&line, &line_len)) { | 301 while (line_reader->GetNextLine(&line, &line_len)) { |
| 219 if (my_strncmp("Tgid:\t", line, 6) == 0) { | 302 if (my_strncmp("Tgid:\t", line, 6) == 0) { |
| 220 my_strtoui(&info->tgid, line + 6); | 303 my_strtoui(&info->tgid, line + 6); |
| 221 } else if (my_strncmp("PPid:\t", line, 6) == 0) { | 304 } else if (my_strncmp("PPid:\t", line, 6) == 0) { |
| 222 my_strtoui(&info->ppid, line + 6); | 305 my_strtoui(&info->ppid, line + 6); |
| 223 } | 306 } |
| 224 | 307 |
| 225 line_reader->PopLine(line_len); | 308 line_reader->PopLine(line_len); |
| 226 } | 309 } |
| 227 | 310 |
| 228 if (info->ppid == -1 || info->tgid == -1) | 311 if (info->ppid == -1 || info->tgid == -1) |
| 229 return false; | 312 return false; |
| 230 | 313 |
| 231 if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 || | 314 if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 || |
| 232 sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { | 315 sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { |
| 233 return false; | 316 return false; |
| 234 } | 317 } |
| 235 | 318 |
| 236 #if defined(__i386) || defined(__x86_64) | 319 #if defined(__i386) || defined(__x86_64) |
| 237 if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) | 320 if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) |
| 238 return false; | 321 return false; |
| 239 | 322 |
| 240 for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { | 323 for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { |
| 241 if (sys_ptrace( | 324 if (sys_ptrace( |
| 242 PTRACE_PEEKUSER, tid, | 325 PTRACE_PEEKUSER, tid, |
| 243 (void *) (offsetof(struct user, | 326 reinterpret_cast<void*> (offsetof(struct user, |
| 244 u_debugreg[0]) + i * sizeof(debugreg_t)), | 327 u_debugreg[0]) + i * |
| 328 sizeof(debugreg_t)), |
| 245 &info->dregs[i]) == -1) { | 329 &info->dregs[i]) == -1) { |
| 246 return false; | 330 return false; |
| 247 } | 331 } |
| 248 } | 332 } |
| 249 #endif | 333 #endif |
| 250 | 334 |
| 251 const uint8_t *stack_pointer; | 335 const uint8_t* stack_pointer; |
| 252 #if defined(__i386) | 336 #if defined(__i386) |
| 253 memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); | 337 memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); |
| 254 #elif defined(__x86_64) | 338 #elif defined(__x86_64) |
| 255 memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); | 339 memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); |
| 256 #else | 340 #else |
| 257 #error "This code hasn't been ported to your platform yet." | 341 #error "This code hasn't been ported to your platform yet." |
| 258 #endif | 342 #endif |
| 259 | 343 |
| 260 if (!GetStackInfo(&info->stack, &info->stack_len, | 344 if (!GetStackInfo(&info->stack, &info->stack_len, |
| 261 (uintptr_t) stack_pointer)) | 345 (uintptr_t) stack_pointer)) |
| 262 return false; | 346 return false; |
| 263 | 347 |
| 264 return true; | 348 return true; |
| 265 } | 349 } |
| 266 | 350 |
| 267 // Get information about the stack, given the stack pointer. We don't try to | 351 // Get information about the stack, given the stack pointer. We don't try to |
| 268 // walk the stack since we might not have all the information needed to do | 352 // walk the stack since we might not have all the information needed to do |
| 269 // unwind. So we just grab, up to, 32k of stack. | 353 // unwind. So we just grab, up to, 32k of stack. |
| 270 bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, | 354 bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, |
| 271 uintptr_t int_stack_pointer) { | 355 uintptr_t int_stack_pointer) { |
| 272 #if defined(__i386) || defined(__x86_64) | 356 #if defined(__i386) || defined(__x86_64) |
| 273 static const bool stack_grows_down = true; | 357 static const bool stack_grows_down = true; |
| 274 static const uintptr_t page_size = 4096; | 358 static const uintptr_t page_size = 4096; |
| 275 #else | 359 #else |
| 276 #error "This code has not been ported to your platform yet." | 360 #error "This code has not been ported to your platform yet." |
| 277 #endif | 361 #endif |
| 278 // Move the stack pointer to the bottom of the page that it's in. | 362 // Move the stack pointer to the bottom of the page that it's in. |
| 279 uint8_t* const stack_pointer = | 363 uint8_t* const stack_pointer = |
| 280 (uint8_t *) (int_stack_pointer & ~(page_size - 1)); | 364 reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); |
| 281 | 365 |
| 282 // The number of bytes of stack which we try to capture. | 366 // The number of bytes of stack which we try to capture. |
| 283 static unsigned kStackToCapture = 32 * 1024; | 367 static unsigned kStackToCapture = 32 * 1024; |
| 284 | 368 |
| 285 const MappingInfo *mapping = FindMapping(stack_pointer); | 369 const MappingInfo* mapping = FindMapping(stack_pointer); |
| 286 if (!mapping) | 370 if (!mapping) |
| 287 return false; | 371 return false; |
| 288 if (stack_grows_down) { | 372 if (stack_grows_down) { |
| 289 const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; | 373 const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; |
| 290 const ptrdiff_t distance_to_end = | 374 const ptrdiff_t distance_to_end = |
| 291 static_cast<ptrdiff_t>(mapping->size) - offset; | 375 static_cast<ptrdiff_t>(mapping->size) - offset; |
| 292 *stack_len = distance_to_end > kStackToCapture ? | 376 *stack_len = distance_to_end > kStackToCapture ? |
| 293 kStackToCapture : distance_to_end; | 377 kStackToCapture : distance_to_end; |
| 294 *stack = stack_pointer; | 378 *stack = stack_pointer; |
| 295 } else { | 379 } else { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 313 while (done < length) { | 397 while (done < length) { |
| 314 const size_t l = length - done > word_size ? word_size : length - done; | 398 const size_t l = length - done > word_size ? word_size : length - done; |
| 315 if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) | 399 if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) |
| 316 tmp = 0; | 400 tmp = 0; |
| 317 memcpy(local + done, &tmp, l); | 401 memcpy(local + done, &tmp, l); |
| 318 done += l; | 402 done += l; |
| 319 } | 403 } |
| 320 } | 404 } |
| 321 | 405 |
| 322 // Find the mapping which the given memory address falls in. | 406 // Find the mapping which the given memory address falls in. |
| 323 const MappingInfo *LinuxDumper::FindMapping(const void *address) const { | 407 const MappingInfo* LinuxDumper::FindMapping(const void* address) const { |
| 324 const uintptr_t addr = (uintptr_t) address; | 408 const uintptr_t addr = (uintptr_t) address; |
| 325 | 409 |
| 326 for (size_t i = 0; i < mappings_.size(); ++i) { | 410 for (size_t i = 0; i < mappings_.size(); ++i) { |
| 327 const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr); | 411 const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr); |
| 328 if (addr >= start && addr - start < mappings_[i]->size) | 412 if (addr >= start && addr - start < mappings_[i]->size) |
| 329 return mappings_[i]; | 413 return mappings_[i]; |
| 330 } | 414 } |
| 331 | 415 |
| 332 return NULL; | 416 return NULL; |
| 333 } | 417 } |
| 334 | 418 |
| 335 } // namespace google_breakpad | 419 } // namespace google_breakpad |
| OLD | NEW |