Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(117)

Side by Side Diff: base/profiler/win32_stack_frame_unwinder.cc

Issue 1263403002: Stack sampling profiler: re-enable stack collection for stacks terminated by leaf functions (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: add comment Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/profiler/win32_stack_frame_unwinder.h"
6
7 #include "base/containers/hash_tables.h"
8 #include "base/memory/singleton.h"
9 #include "base/stl_util.h"
10
11 namespace base {
12
13 // LeafUnwindBlacklist --------------------------------------------------------
14
15 namespace {
16
17 // Records modules that are known to have functions that violate the Microsoft
18 // x64 calling convention and would be dangerous to manually unwind if
19 // encountered as the last frame on the call stack.
cpu_(ooo_6.6-7.5) 2015/08/19 19:07:24 can you add an example of this kind of function?
Mike Wittman 2015/08/19 19:54:41 Done.
20 class LeafUnwindBlacklist {
21 public:
22 static LeafUnwindBlacklist* GetInstance();
23
24 // This function does not allocate memory and is safe to call between
25 // SuspendThread and ResumeThread.
26 bool IsBlacklisted(const void* module) const;
27
28 // Allocates memory. Must be invoked only after ResumeThread, otherwise
29 // RecordStack will eventually deadlock on a heap lock.
cpu_(ooo_6.6-7.5) 2015/08/19 19:07:24 we can make RecoardStack allocate from a different
Mike Wittman 2015/08/19 19:54:41 Ah, I didn't mean for this comment to imply that R
30 void AddModuleToBlacklist(const void* module);
31
32 private:
33 friend struct DefaultSingletonTraits<LeafUnwindBlacklist>;
34
35 LeafUnwindBlacklist();
36 ~LeafUnwindBlacklist();
37
38 // The set of modules known to have functions that violate the Microsoft x64
39 // calling convention.
40 base::hash_set<const void*> blacklisted_modules_;
41
42 DISALLOW_COPY_AND_ASSIGN(LeafUnwindBlacklist);
43 };
44
45 // static
46 LeafUnwindBlacklist* LeafUnwindBlacklist::GetInstance() {
47 // Leaky for shutdown performance.
48 return Singleton<LeafUnwindBlacklist,
49 LeakySingletonTraits<LeafUnwindBlacklist>>::get();
50 }
51
52 bool LeafUnwindBlacklist::IsBlacklisted(const void* module) const {
53 return ContainsKey(blacklisted_modules_, module);
54 }
55
56 void LeafUnwindBlacklist::AddModuleToBlacklist(const void* module) {
57 CHECK(module);
58 blacklisted_modules_.insert(module);
59 }
60
61 LeafUnwindBlacklist::LeafUnwindBlacklist() {}
62 LeafUnwindBlacklist::~LeafUnwindBlacklist() {}
63
64 } // namespace
65
66 // Win32StackFrameUnwinder ----------------------------------------------------
67
68 Win32StackFrameUnwinder::UnwindFunctions::~UnwindFunctions() {}
69 Win32StackFrameUnwinder::UnwindFunctions::UnwindFunctions() {}
70
71 Win32StackFrameUnwinder::Win32UnwindFunctions::Win32UnwindFunctions() {}
72
73 PRUNTIME_FUNCTION Win32StackFrameUnwinder::Win32UnwindFunctions::
74 LookupFunctionEntry(DWORD64 program_counter, PDWORD64 image_base) {
75 #ifdef _WIN64
76 return RtlLookupFunctionEntry(program_counter, image_base, nullptr);
77 #else
78 NOTREACHED();
79 return nullptr;
80 #endif
81 }
82
83 void Win32StackFrameUnwinder::Win32UnwindFunctions::VirtualUnwind(
84 DWORD64 image_base,
85 DWORD64 program_counter,
86 PRUNTIME_FUNCTION runtime_function,
87 CONTEXT* context) {
88 #ifdef _WIN64
89 void* handler_data;
90 ULONG64 establisher_frame;
91 KNONVOLATILE_CONTEXT_POINTERS nvcontext = {};
92 RtlVirtualUnwind(0, image_base, program_counter, runtime_function,
93 context, &handler_data, &establisher_frame, &nvcontext);
94 #else
95 NOTREACHED();
96 #endif
97 }
98
99
100 Win32StackFrameUnwinder::Win32StackFrameUnwinder()
101 : Win32StackFrameUnwinder(&win32_unwind_functions_) {
102 }
103
104 Win32StackFrameUnwinder::~Win32StackFrameUnwinder() {
105 if (pending_blacklisted_module_) {
106 LeafUnwindBlacklist::GetInstance()->AddModuleToBlacklist(
107 pending_blacklisted_module_);
108 }
109 }
110
111 bool Win32StackFrameUnwinder::TryUnwind(CONTEXT* context) {
112 #ifdef _WIN64
113 CHECK(!at_top_frame_ || unwind_info_present_for_all_frames_);
114 CHECK(!pending_blacklisted_module_);
115
116 ULONG64 image_base;
117 // Try to look up unwind metadata for the current function.
118 PRUNTIME_FUNCTION runtime_function =
119 unwind_functions_->LookupFunctionEntry(context->Rip, &image_base);
120
121 if (runtime_function) {
122 unwind_functions_->VirtualUnwind(image_base, context->Rip, runtime_function,
123 context);
124 at_top_frame_ = false;
125 } else {
126 // RtlLookupFunctionEntry didn't find unwind information. This could mean
127 // the code at the instruction pointer is in:
128 //
129 // 1. a true leaf function (i.e. a function that neither calls a function,
130 // nor allocates any stack space itself) in which case the return
131 // address is at RSP, or
132 //
133 // 2. a function that doesn't adhere to the Microsoft x64 calling
134 // convention, either by not providing the required unwind information,
135 // or by not having the prologue or epilogue required for unwinding;
136 // this case has been observed in crash data in injected third party
137 // DLLs.
138 //
139 // In valid code, case 1 can only occur (by definition) as the last frame
140 // on the stack. This happens in about 5% of observed stacks and can
141 // easily be unwound by popping RSP and using it as the next frame's
142 // instruction pointer.
143 //
144 // Case 2 can occur anywhere on the stack, and attempting to unwind the
145 // stack will result in treating whatever value happens to be on the stack
146 // at RSP as the next frame's instruction pointer. This is certainly wrong
147 // and very likely to lead to crashing by deferencing invalid pointers in
148 // the next RtlVirtualUnwind call.
149 //
150 // If we see case 2 at a location not the last frame, and all the previous
151 // frame had valid unwind information, then this is definitely bad code.
152 // We blacklist the module as untrustable for unwinding if we encounter a
153 // function in it that doesn't have unwind information.
154
155 if (at_top_frame_) {
156 at_top_frame_ = false;
157
158 // We are at the end of the stack. It's very likely that we're in case 1
159 // since the vast majority of code adheres to the Microsoft x64 calling
160 // convention. But there's a small chance we might be unlucky and be in
161 // case 2. If this module is known to have bad code according to the
162 // leaf unwind blacklist, stop here, otherwise manually unwind.
163 if (LeafUnwindBlacklist::GetInstance()->IsBlacklisted(
164 reinterpret_cast<const void*>(image_base))) {
165 return false;
166 }
167
168 context->Rip = context->Rsp;
169 context->Rsp += 8;
170 unwind_info_present_for_all_frames_ = false;
171 } else {
172 // We're not at the end of the stack. This frame is untrustworthy and we
173 // can't safely unwind from here.
174 if (unwind_info_present_for_all_frames_) {
175 // Unwind information was present for all previous frames, so we can
176 // be confident this is case 2. Record the module to be blacklisted.
177 pending_blacklisted_module_ =
178 reinterpret_cast<const void *>(image_base);
179 } else {
180 // We started off on a function without unwind information. It's very
181 // likely that all frames up to this point have been good, and this
182 // frame is case 2. But it's possible that the initial frame was case
183 // 2 but hadn't been blacklisted yet, and we've started to go off into
184 // the weeds. Since we can't be sure, just bail out without
185 // blacklisting the module; chances are we'll later encounter the same
186 // function on a stack with full unwind information.
187 }
188 return false;
189 }
190 }
191
192 return true;
193 #else
194 NOTREACHED();
195 return false;
196 #endif
197 }
198
199 Win32StackFrameUnwinder::Win32StackFrameUnwinder(
200 UnwindFunctions* unwind_functions)
201 : at_top_frame_(true),
202 unwind_info_present_for_all_frames_(true),
203 pending_blacklisted_module_(nullptr),
204 unwind_functions_(unwind_functions) {
205 }
206
207 } // namespace base
OLDNEW
« no previous file with comments | « base/profiler/win32_stack_frame_unwinder.h ('k') | base/profiler/win32_stack_frame_unwinder_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698