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 |