Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/profiler/native_stack_sampler.h" | 5 #include "base/profiler/native_stack_sampler.h" |
| 6 | 6 |
| 7 #include <dlfcn.h> | 7 #include <dlfcn.h> |
| 8 #include <libkern/OSByteOrder.h> | 8 #include <libkern/OSByteOrder.h> |
| 9 #include <libunwind.h> | 9 #include <libunwind.h> |
| 10 #include <mach-o/compact_unwind_encoding.h> | |
| 10 #include <mach-o/swap.h> | 11 #include <mach-o/swap.h> |
| 11 #include <mach/kern_return.h> | 12 #include <mach/kern_return.h> |
| 12 #include <mach/mach.h> | 13 #include <mach/mach.h> |
| 13 #include <mach/thread_act.h> | 14 #include <mach/thread_act.h> |
| 14 #include <pthread.h> | 15 #include <pthread.h> |
| 15 #include <sys/resource.h> | 16 #include <sys/resource.h> |
| 16 #include <sys/syslimits.h> | 17 #include <sys/syslimits.h> |
| 17 | 18 |
| 18 #include <algorithm> | 19 #include <algorithm> |
| 19 #include <map> | 20 #include <map> |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 106 *reg = RewritePointerIfInOriginalStack( | 107 *reg = RewritePointerIfInOriginalStack( |
| 107 original_stack_bottom, original_stack_top, stack_copy_bottom, *reg); | 108 original_stack_bottom, original_stack_top, stack_copy_bottom, *reg); |
| 108 } | 109 } |
| 109 } | 110 } |
| 110 | 111 |
| 111 // Walks the stack represented by |unwind_context|, calling back to the provided | 112 // Walks the stack represented by |unwind_context|, calling back to the provided |
| 112 // lambda for each frame. Returns false if an error occurred, otherwise returns | 113 // lambda for each frame. Returns false if an error occurred, otherwise returns |
| 113 // true. | 114 // true. |
| 114 template <typename StackFrameCallback> | 115 template <typename StackFrameCallback> |
| 115 bool WalkStackFromContext(unw_context_t* unwind_context, | 116 bool WalkStackFromContext(unw_context_t* unwind_context, |
| 117 uintptr_t stack_top, | |
| 116 size_t* frame_count, | 118 size_t* frame_count, |
| 117 const StackFrameCallback& callback) { | 119 const StackFrameCallback& callback) { |
| 118 unw_cursor_t unwind_cursor; | 120 unw_cursor_t unwind_cursor; |
| 119 unw_init_local(&unwind_cursor, unwind_context); | 121 unw_init_local(&unwind_cursor, unwind_context); |
| 120 | 122 |
| 121 int step_result; | 123 int step_result; |
| 122 unw_word_t ip; | 124 unw_word_t ip; |
| 123 do { | 125 do { |
| 124 ++(*frame_count); | 126 ++(*frame_count); |
| 125 unw_get_reg(&unwind_cursor, UNW_REG_IP, &ip); | 127 unw_get_reg(&unwind_cursor, UNW_REG_IP, &ip); |
| 126 | 128 |
| 127 callback(static_cast<uintptr_t>(ip)); | 129 callback(static_cast<uintptr_t>(ip)); |
| 128 | 130 |
| 131 // If this stack frame has a frame pointer, stepping the cursor will involve | |
|
Mike Wittman
2017/05/19 21:21:21
nit: this could be extracted to a separate functio
Avi (use Gerrit)
2017/05/22 19:03:37
It could be, but it's short enough and is only use
Mike Wittman
2017/05/22 19:19:58
I was thinking it would make this function a littl
| |
| 132 // indexing memory access off of that pointer. In that case, sanity-check | |
|
Mark Mentovai
2017/05/22 20:29:44
Does unw_init_remote() work instead of unw_init_lo
Avi (use Gerrit)
2017/05/22 20:32:35
I saw that when looking at the source. Dunno why t
| |
| 133 // the frame pointer register to ensure it's within bounds. | |
| 134 unw_proc_info_t proc_info; | |
| 135 unw_get_proc_info(&unwind_cursor, &proc_info); | |
| 136 if ((proc_info.format & UNWIND_X86_64_MODE_MASK) == | |
| 137 UNWIND_X86_64_MODE_RBP_FRAME) { | |
| 138 unw_word_t rsp, rbp; | |
| 139 unw_get_reg(&unwind_cursor, UNW_X86_64_RSP, &rsp); | |
| 140 unw_get_reg(&unwind_cursor, UNW_X86_64_RBP, &rbp); | |
| 141 if (rbp < rsp || rbp > stack_top) | |
| 142 return false; | |
| 143 } | |
| 144 | |
| 129 step_result = unw_step(&unwind_cursor); | 145 step_result = unw_step(&unwind_cursor); |
| 130 } while (step_result > 0); | 146 } while (step_result > 0); |
| 131 | 147 |
| 132 if (step_result != 0) | 148 if (step_result != 0) |
| 133 return false; | 149 return false; |
| 134 | 150 |
| 135 return true; | 151 return true; |
| 136 } | 152 } |
| 137 | 153 |
| 138 const char* LibSystemKernelName() { | 154 const char* LibSystemKernelName() { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 165 | 181 |
| 166 // Problem 1: There is no official way to create a unw_context other than to | 182 // Problem 1: There is no official way to create a unw_context other than to |
| 167 // create it from the current state of the current thread's stack. To get | 183 // create it from the current state of the current thread's stack. To get |
| 168 // around this, forge a context. A unw_context is just a copy of the 16 main | 184 // around this, forge a context. A unw_context is just a copy of the 16 main |
| 169 // registers followed by the instruction pointer, nothing more. | 185 // registers followed by the instruction pointer, nothing more. |
| 170 // Coincidentally, the first 17 items of the x86_thread_state64_t type are | 186 // Coincidentally, the first 17 items of the x86_thread_state64_t type are |
| 171 // exactly those registers in exactly the same order, so just bulk copy them | 187 // exactly those registers in exactly the same order, so just bulk copy them |
| 172 // over. | 188 // over. |
| 173 unw_context_t unwind_context; | 189 unw_context_t unwind_context; |
| 174 memcpy(&unwind_context, &thread_state, sizeof(uintptr_t) * 17); | 190 memcpy(&unwind_context, &thread_state, sizeof(uintptr_t) * 17); |
| 175 bool result = WalkStackFromContext(&unwind_context, &frame_count, callback); | 191 bool result = |
| 192 WalkStackFromContext(&unwind_context, stack_top, &frame_count, callback); | |
| 176 | 193 |
| 177 if (!result) | 194 if (!result) |
| 178 return; | 195 return; |
| 179 | 196 |
| 180 if (frame_count == 1) { | 197 if (frame_count == 1) { |
| 181 // Problem 2: Because libunwind is designed to be triggered by user code on | 198 // Problem 2: Because libunwind is designed to be triggered by user code on |
| 182 // their own thread, if it hits a library that has no unwind info for the | 199 // their own thread, if it hits a library that has no unwind info for the |
| 183 // function that is being executed, it just stops. This isn't a problem in | 200 // function that is being executed, it just stops. This isn't a problem in |
| 184 // the normal case, but in this case, it's quite possible that the stack | 201 // the normal case, but in this case, it's quite possible that the stack |
| 185 // being walked is stopped in a function that bridges to the kernel and thus | 202 // being walked is stopped in a function that bridges to the kernel and thus |
| 186 // is missing the unwind info. | 203 // is missing the unwind info. |
| 187 | 204 |
| 188 // For now, just unwind the single case where the thread is stopped in a | 205 // For now, just unwind the single case where the thread is stopped in a |
| 189 // function in libsystem_kernel. | 206 // function in libsystem_kernel. |
| 190 uint64_t& rsp = unwind_context.data[7]; | 207 uint64_t& rsp = unwind_context.data[7]; |
| 191 uint64_t& rip = unwind_context.data[16]; | 208 uint64_t& rip = unwind_context.data[16]; |
| 192 Dl_info info; | 209 Dl_info info; |
| 193 if (dladdr(reinterpret_cast<void*>(rip), &info) != 0 && | 210 if (dladdr(reinterpret_cast<void*>(rip), &info) != 0 && |
| 194 strcmp(info.dli_fname, LibSystemKernelName()) == 0) { | 211 strcmp(info.dli_fname, LibSystemKernelName()) == 0) { |
| 195 rip = *reinterpret_cast<uint64_t*>(rsp); | 212 rip = *reinterpret_cast<uint64_t*>(rsp); |
| 196 rsp += 8; | 213 rsp += 8; |
| 197 WalkStackFromContext(&unwind_context, &frame_count, callback); | 214 WalkStackFromContext(&unwind_context, stack_top, &frame_count, callback); |
| 198 } | 215 } |
| 199 } | 216 } |
| 200 } | 217 } |
| 201 | 218 |
| 202 // Module identifiers --------------------------------------------------------- | 219 // Module identifiers --------------------------------------------------------- |
| 203 | 220 |
| 204 // Returns the unique build ID for a module loaded at |module_addr|. Returns the | 221 // Returns the unique build ID for a module loaded at |module_addr|. Returns the |
| 205 // empty string if the function fails to get the build ID. | 222 // empty string if the function fails to get the build ID. |
| 206 // | 223 // |
| 207 // Build IDs are created by the concatenation of the module's GUID (Windows) / | 224 // Build IDs are created by the concatenation of the module's GUID (Windows) / |
| (...skipping 249 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 457 stack_rlimit.rlim_cur != RLIM_INFINITY) { | 474 stack_rlimit.rlim_cur != RLIM_INFINITY) { |
| 458 return stack_rlimit.rlim_cur; | 475 return stack_rlimit.rlim_cur; |
| 459 } | 476 } |
| 460 | 477 |
| 461 // If getrlimit somehow fails, return the default macOS main thread stack size | 478 // If getrlimit somehow fails, return the default macOS main thread stack size |
| 462 // of 8 MB (DFLSSIZ in <i386/vmparam.h>) with extra wiggle room. | 479 // of 8 MB (DFLSSIZ in <i386/vmparam.h>) with extra wiggle room. |
| 463 return 12 * 1024 * 1024; | 480 return 12 * 1024 * 1024; |
| 464 } | 481 } |
| 465 | 482 |
| 466 } // namespace base | 483 } // namespace base |
| OLD | NEW |