Chromium Code Reviews| 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 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 73 | 73 |
| 74 // Resume a thread by detaching from it. | 74 // Resume a thread by detaching from it. |
| 75 static bool ResumeThread(pid_t pid) { | 75 static bool ResumeThread(pid_t pid) { |
| 76 return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; | 76 return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; |
| 77 } | 77 } |
| 78 | 78 |
| 79 namespace google_breakpad { | 79 namespace google_breakpad { |
| 80 | 80 |
| 81 LinuxDumper::LinuxDumper(int pid) | 81 LinuxDumper::LinuxDumper(int pid) |
| 82 : pid_(pid), | 82 : pid_(pid), |
| 83 threads_suspened_(false), | 83 threads_suspended_(false), |
| 84 threads_(&allocator_, 8), | 84 threads_(&allocator_, 8), |
| 85 mappings_(&allocator_) { | 85 mappings_(&allocator_) { |
| 86 } | 86 } |
| 87 | 87 |
| 88 bool LinuxDumper::Init() { | 88 bool LinuxDumper::Init() { |
| 89 return EnumerateThreads(&threads_) && | 89 return EnumerateThreads(&threads_) && |
| 90 EnumerateMappings(&mappings_); | 90 EnumerateMappings(&mappings_); |
| 91 } | 91 } |
| 92 | 92 |
| 93 bool LinuxDumper::ThreadsSuspend() { | 93 bool LinuxDumper::ThreadsSuspend() { |
| 94 if (threads_suspened_) | 94 if (threads_suspended_) |
| 95 return true; | 95 return true; |
| 96 bool good = true; | 96 bool good = true; |
| 97 for (size_t i = 0; i < threads_.size(); ++i) | 97 for (size_t i = 0; i < threads_.size(); ++i) |
| 98 good &= SuspendThread(threads_[i]); | 98 good &= SuspendThread(threads_[i]); |
| 99 threads_suspened_ = true; | 99 threads_suspended_ = true; |
| 100 return good; | 100 return good; |
| 101 } | 101 } |
| 102 | 102 |
| 103 bool LinuxDumper::ThreadsResume() { | 103 bool LinuxDumper::ThreadsResume() { |
| 104 if (!threads_suspened_) | 104 if (!threads_suspended_) |
| 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_suspended_ = false; |
| 110 return good; | 110 return good; |
| 111 } | 111 } |
| 112 | 112 |
| 113 void | 113 void |
| 114 LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { | 114 LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { |
| 115 assert(path); | 115 assert(path); |
| 116 if (!path) { | 116 if (!path) { |
| 117 return; | 117 return; |
| 118 } | 118 } |
| 119 | 119 |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 273 } | 273 } |
| 274 } | 274 } |
| 275 dir_reader->PopEntry(); | 275 dir_reader->PopEntry(); |
| 276 } | 276 } |
| 277 | 277 |
| 278 sys_close(fd); | 278 sys_close(fd); |
| 279 return true; | 279 return true; |
| 280 } | 280 } |
| 281 | 281 |
| 282 // Read thread info from /proc/$pid/status. | 282 // Read thread info from /proc/$pid/status. |
| 283 // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible, | 283 // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, |
| 284 // 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 |
| 285 // availible. | 285 // available. |
| 286 bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { | 286 bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { |
| 287 assert(info != NULL); | 287 assert(info != NULL); |
| 288 char status_path[80]; | 288 char status_path[80]; |
| 289 BuildProcPath(status_path, tid, "status"); | 289 BuildProcPath(status_path, tid, "status"); |
| 290 | 290 |
| 291 const int fd = open(status_path, O_RDONLY); | 291 const int fd = open(status_path, O_RDONLY); |
| 292 if (fd < 0) | 292 if (fd < 0) |
| 293 return false; | 293 return false; |
| 294 | 294 |
| 295 LineReader* const line_reader = new(allocator_) LineReader(fd); | 295 LineReader* const line_reader = new(allocator_) LineReader(fd); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 330 return false; | 330 return false; |
| 331 } | 331 } |
| 332 } | 332 } |
| 333 #endif | 333 #endif |
| 334 | 334 |
| 335 const uint8_t* stack_pointer; | 335 const uint8_t* stack_pointer; |
| 336 #if defined(__i386) | 336 #if defined(__i386) |
| 337 memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); | 337 memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); |
| 338 #elif defined(__x86_64) | 338 #elif defined(__x86_64) |
| 339 memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); | 339 memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); |
| 340 #elif defined(__ARM_EABI__) | |
| 341 memcpy(&stack_pointer, &info->regs.uregs[R13], sizeof(info->regs.uregs[R13])); | |
| 340 #else | 342 #else |
| 341 #error "This code hasn't been ported to your platform yet." | 343 #error "This code hasn't been ported to your platform yet." |
| 342 #endif | 344 #endif |
| 343 | 345 |
| 344 if (!GetStackInfo(&info->stack, &info->stack_len, | 346 if (!GetStackInfo(&info->stack, &info->stack_len, |
| 345 (uintptr_t) stack_pointer)) | 347 (uintptr_t) stack_pointer)) |
| 346 return false; | 348 return false; |
| 347 | 349 |
| 348 return true; | 350 return true; |
| 349 } | 351 } |
| 350 | 352 |
| 351 // Get information about the stack, given the stack pointer. We don't try to | 353 // Get information about the stack, given the stack pointer. We don't try to |
| 352 // walk the stack since we might not have all the information needed to do | 354 // walk the stack since we might not have all the information needed to do |
| 353 // unwind. So we just grab, up to, 32k of stack. | 355 // unwind. So we just grab, up to, 32k of stack. |
| 354 bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, | 356 bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, |
| 355 uintptr_t int_stack_pointer) { | 357 uintptr_t int_stack_pointer) { |
| 356 #if defined(__i386) || defined(__x86_64) | |
| 357 static const bool stack_grows_down = true; | |
| 358 static const uintptr_t page_size = 4096; | |
| 359 #else | |
| 360 #error "This code has not been ported to your platform yet." | |
| 361 #endif | |
| 362 // Move the stack pointer to the bottom of the page that it's in. | 358 // Move the stack pointer to the bottom of the page that it's in. |
| 359 const uintptr_t page_size = getpagesize(); | |
| 360 | |
| 363 uint8_t* const stack_pointer = | 361 uint8_t* const stack_pointer = |
| 364 reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); | 362 reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); |
| 365 | 363 |
| 366 // The number of bytes of stack which we try to capture. | 364 // The number of bytes of stack which we try to capture. |
| 367 static unsigned kStackToCapture = 32 * 1024; | 365 static int kStackToCapture = 32 * 1024; |
| 368 | 366 |
| 369 const MappingInfo* mapping = FindMapping(stack_pointer); | 367 const MappingInfo* mapping = FindMapping(stack_pointer); |
| 370 if (!mapping) | 368 if (!mapping) |
| 371 return false; | 369 return false; |
| 372 if (stack_grows_down) { | 370 const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; |
|
awong
2010/02/08 20:52:55
so...we're assuming a single stack direction now?
| |
| 373 const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; | 371 const ptrdiff_t distance_to_end = |
| 374 const ptrdiff_t distance_to_end = | 372 static_cast<ptrdiff_t>(mapping->size) - offset; |
| 375 static_cast<ptrdiff_t>(mapping->size) - offset; | 373 *stack_len = distance_to_end > kStackToCapture ? |
| 376 *stack_len = distance_to_end > kStackToCapture ? | 374 kStackToCapture : distance_to_end; |
| 377 kStackToCapture : distance_to_end; | 375 *stack = stack_pointer; |
| 378 *stack = stack_pointer; | |
| 379 } else { | |
| 380 const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; | |
| 381 *stack_len = offset > kStackToCapture ? kStackToCapture : offset; | |
| 382 *stack = stack_pointer - *stack_len; | |
| 383 } | |
| 384 | |
| 385 return true; | 376 return true; |
| 386 } | 377 } |
| 387 | 378 |
| 388 // static | 379 // static |
| 389 void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, | 380 void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, |
| 390 size_t length) { | 381 size_t length) { |
| 391 unsigned long tmp; | 382 unsigned long tmp = 55; |
|
awong
2010/02/08 20:52:55
Magic number alert! What's this number for?
| |
| 392 size_t done = 0; | 383 size_t done = 0; |
| 393 static const size_t word_size = sizeof(tmp); | 384 static const size_t word_size = sizeof(tmp); |
| 394 uint8_t* const local = (uint8_t*) dest; | 385 uint8_t* const local = (uint8_t*) dest; |
| 395 uint8_t* const remote = (uint8_t*) src; | 386 uint8_t* const remote = (uint8_t*) src; |
| 396 | 387 |
| 397 while (done < length) { | 388 while (done < length) { |
| 398 const size_t l = length - done > word_size ? word_size : length - done; | 389 const size_t l = length - done > word_size ? word_size : length - done; |
| 399 if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) | 390 if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { |
| 400 tmp = 0; | 391 tmp = 0; |
| 392 } | |
| 401 memcpy(local + done, &tmp, l); | 393 memcpy(local + done, &tmp, l); |
| 402 done += l; | 394 done += l; |
| 403 } | 395 } |
| 404 } | 396 } |
| 405 | 397 |
| 406 // Find the mapping which the given memory address falls in. | 398 // Find the mapping which the given memory address falls in. |
| 407 const MappingInfo* LinuxDumper::FindMapping(const void* address) const { | 399 const MappingInfo* LinuxDumper::FindMapping(const void* address) const { |
| 408 const uintptr_t addr = (uintptr_t) address; | 400 const uintptr_t addr = (uintptr_t) address; |
| 409 | 401 |
| 410 for (size_t i = 0; i < mappings_.size(); ++i) { | 402 for (size_t i = 0; i < mappings_.size(); ++i) { |
| 411 const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr); | 403 const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr); |
| 412 if (addr >= start && addr - start < mappings_[i]->size) | 404 if (addr >= start && addr - start < mappings_[i]->size) |
| 413 return mappings_[i]; | 405 return mappings_[i]; |
| 414 } | 406 } |
| 415 | 407 |
| 416 return NULL; | 408 return NULL; |
| 417 } | 409 } |
| 418 | 410 |
| 419 } // namespace google_breakpad | 411 } // namespace google_breakpad |
| OLD | NEW |