OLD | NEW |
---|---|
1 // Copyright 2015 The Crashpad Authors. All rights reserved. | 1 // Copyright 2015 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
109 } | 109 } |
110 return handle; | 110 return handle; |
111 } | 111 } |
112 | 112 |
113 // It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a | 113 // It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a |
114 // side-effect of returning the SuspendCount of the thread on success, so we | 114 // side-effect of returning the SuspendCount of the thread on success, so we |
115 // fill out these two pieces of semi-unrelated data in the same function. | 115 // fill out these two pieces of semi-unrelated data in the same function. |
116 template <class Traits> | 116 template <class Traits> |
117 bool FillThreadContextAndSuspendCount(HANDLE thread_handle, | 117 bool FillThreadContextAndSuspendCount(HANDLE thread_handle, |
118 ProcessReaderWin::Thread* thread, | 118 ProcessReaderWin::Thread* thread, |
119 ProcessSuspensionState suspension_state) { | 119 ProcessSuspensionState suspension_state, |
120 bool is_wow64) { | |
120 // Don't suspend the thread if it's this thread. This is really only for test | 121 // Don't suspend the thread if it's this thread. This is really only for test |
121 // binaries, as we won't be walking ourselves, in general. | 122 // binaries, as we won't be walking ourselves, in general. |
122 bool is_current_thread = thread->id == | 123 bool is_current_thread = thread->id == |
123 reinterpret_cast<process_types::TEB<Traits>*>( | 124 reinterpret_cast<process_types::TEB<Traits>*>( |
124 NtCurrentTeb())->ClientId.UniqueThread; | 125 NtCurrentTeb())->ClientId.UniqueThread; |
125 | 126 |
126 // TODO(scottmg): Handle cross-bitness in this function. | |
127 | |
128 if (is_current_thread) { | 127 if (is_current_thread) { |
129 DCHECK(suspension_state == ProcessSuspensionState::kRunning); | 128 DCHECK(suspension_state == ProcessSuspensionState::kRunning); |
130 thread->suspend_count = 0; | 129 thread->suspend_count = 0; |
131 RtlCaptureContext(&thread->context); | 130 DCHECK(!is_wow64); |
Mark Mentovai
2015/09/18 15:44:07
Why not?
(After reading more of this file) Oh, I
scottmg
2015/09/18 19:45:26
I'm not quite sure what you mean, is that a better
| |
131 RtlCaptureContext(&thread->context_native); | |
132 } else { | 132 } else { |
133 DWORD previous_suspend_count = SuspendThread(thread_handle); | 133 DWORD previous_suspend_count = SuspendThread(thread_handle); |
134 if (previous_suspend_count == -1) { | 134 if (previous_suspend_count == -1) { |
135 PLOG(ERROR) << "SuspendThread failed"; | 135 PLOG(ERROR) << "SuspendThread failed"; |
136 return false; | 136 return false; |
137 } | 137 } |
138 DCHECK(previous_suspend_count > 0 || | 138 DCHECK(previous_suspend_count > 0 || |
139 suspension_state == ProcessSuspensionState::kRunning); | 139 suspension_state == ProcessSuspensionState::kRunning); |
140 thread->suspend_count = | 140 thread->suspend_count = |
141 previous_suspend_count - | 141 previous_suspend_count - |
142 (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0); | 142 (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0); |
143 | 143 |
144 memset(&thread->context, 0, sizeof(thread->context)); | 144 #if defined(ARCH_CPU_64_BITS) |
145 thread->context.ContextFlags = CONTEXT_ALL; | 145 if (is_wow64) { |
146 if (!GetThreadContext(thread_handle, &thread->context)) { | 146 memset(&thread->context_wow64, 0, sizeof(thread->context_wow64)); |
Mark Mentovai
2015/09/18 15:44:07
Maybe give the union a name so that you can memset
scottmg
2015/09/18 19:45:26
Done.
| |
147 PLOG(ERROR) << "GetThreadContext failed"; | 147 thread->context_wow64.ContextFlags = CONTEXT_ALL; |
148 return false; | 148 if (!Wow64GetThreadContext(thread_handle, &thread->context_wow64)) { |
Mark Mentovai
2015/09/18 15:44:06
Isn’t this Vista and later?
scottmg
2015/09/18 19:45:26
Yes, but it's in ARCH_CPU_64_BITS, and we've never
| |
149 PLOG(ERROR) << "Wow64GetThreadContext failed"; | |
150 return false; | |
151 } | |
152 } else { | |
153 #endif // ARCH_CPU_64_BITS | |
154 memset(&thread->context_native, 0, sizeof(thread->context_native)); | |
155 thread->context_native.ContextFlags = CONTEXT_ALL; | |
156 if (!GetThreadContext(thread_handle, &thread->context_native)) { | |
157 PLOG(ERROR) << "GetThreadContext failed"; | |
158 return false; | |
159 } | |
160 #if defined(ARCH_CPU_64_BITS) | |
Mark Mentovai
2015/09/18 15:44:06
Maybe do the const bool thing I suggested before t
scottmg
2015/09/18 19:45:26
Is that what you were thinking?
| |
149 } | 161 } |
162 #endif // ARCH_CPU_64_BITS | |
150 | 163 |
151 if (!ResumeThread(thread_handle)) { | 164 if (!ResumeThread(thread_handle)) { |
152 PLOG(ERROR) << "ResumeThread failed"; | 165 PLOG(ERROR) << "ResumeThread failed"; |
153 return false; | 166 return false; |
154 } | 167 } |
155 } | 168 } |
156 | 169 |
157 return true; | 170 return true; |
158 } | 171 } |
159 | 172 |
160 } // namespace | 173 } // namespace |
161 | 174 |
162 ProcessReaderWin::Thread::Thread() | 175 ProcessReaderWin::Thread::Thread() |
163 : context(), | 176 : context_native(), |
177 #if defined(ARCH_CPU_64_BITS) | |
178 context_wow64(), | |
Mark Mentovai
2015/09/18 15:44:07
If the union is named, you should just initialize
scottmg
2015/09/18 19:45:26
Done.
| |
179 #endif // ARCH_CPU_64_BITS | |
164 id(0), | 180 id(0), |
165 teb(0), | 181 teb(0), |
166 stack_region_address(0), | 182 stack_region_address(0), |
167 stack_region_size(0), | 183 stack_region_size(0), |
168 suspend_count(0), | 184 suspend_count(0), |
169 priority_class(0), | 185 priority_class(0), |
170 priority(0) { | 186 priority(0) { |
171 } | 187 } |
172 | 188 |
173 ProcessReaderWin::ProcessReaderWin() | 189 ProcessReaderWin::ProcessReaderWin() |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
235 } | 251 } |
236 | 252 |
237 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { | 253 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { |
238 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 254 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
239 | 255 |
240 if (initialized_threads_) | 256 if (initialized_threads_) |
241 return threads_; | 257 return threads_; |
242 | 258 |
243 initialized_threads_ = true; | 259 initialized_threads_ = true; |
244 | 260 |
245 if (process_info_.Is64Bit()) | 261 #if ARCH_CPU_64_BITS |
Mark Mentovai
2015/09/18 15:44:06
defined()
scottmg
2015/09/18 19:45:26
Done.
| |
246 ReadThreadData<process_types::internal::Traits64>(); | 262 ReadThreadData<process_types::internal::Traits64>(process_info_.IsWow64()); |
247 else | 263 #else |
248 ReadThreadData<process_types::internal::Traits32>(); | 264 ReadThreadData<process_types::internal::Traits32>(false); |
265 #endif | |
249 | 266 |
250 return threads_; | 267 return threads_; |
251 } | 268 } |
252 | 269 |
253 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() { | 270 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() { |
254 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 271 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
255 | 272 |
256 if (!process_info_.Modules(&modules_)) { | 273 if (!process_info_.Modules(&modules_)) { |
257 LOG(ERROR) << "couldn't retrieve modules"; | 274 LOG(ERROR) << "couldn't retrieve modules"; |
258 } | 275 } |
259 | 276 |
260 return modules_; | 277 return modules_; |
261 } | 278 } |
262 | 279 |
263 template <class Traits> | 280 template <class Traits> |
264 void ProcessReaderWin::ReadThreadData() { | 281 void ProcessReaderWin::ReadThreadData(bool is_wow64) { |
265 DCHECK(threads_.empty()); | 282 DCHECK(threads_.empty()); |
266 | 283 |
267 scoped_ptr<uint8_t[]> buffer; | 284 scoped_ptr<uint8_t[]> buffer; |
268 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information = | 285 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information = |
269 GetProcessInformation<Traits>(process_, &buffer); | 286 GetProcessInformation<Traits>(process_, &buffer); |
270 if (!process_information) | 287 if (!process_information) |
271 return; | 288 return; |
272 | 289 |
273 for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { | 290 for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { |
274 const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info = | 291 const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info = |
275 process_information->Threads[i]; | 292 process_information->Threads[i]; |
276 ProcessReaderWin::Thread thread; | 293 ProcessReaderWin::Thread thread; |
277 thread.id = thread_info.ClientId.UniqueThread; | 294 thread.id = thread_info.ClientId.UniqueThread; |
278 | 295 |
279 ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); | 296 ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); |
280 if (!thread_handle.is_valid()) | 297 if (!thread_handle.is_valid()) |
281 continue; | 298 continue; |
282 | 299 |
283 if (!FillThreadContextAndSuspendCount<Traits>( | 300 if (!FillThreadContextAndSuspendCount<Traits>( |
284 thread_handle.get(), &thread, suspension_state_)) { | 301 thread_handle.get(), &thread, suspension_state_, is_wow64)) { |
285 continue; | 302 continue; |
286 } | 303 } |
287 | 304 |
288 // TODO(scottmg): I believe we could reverse engineer the PriorityClass from | 305 // TODO(scottmg): I believe we could reverse engineer the PriorityClass from |
289 // the Priority, BasePriority, and | 306 // the Priority, BasePriority, and |
290 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 . | 307 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 . |
291 // MinidumpThreadWriter doesn't handle it yet in any case, so investigate | 308 // MinidumpThreadWriter doesn't handle it yet in any case, so investigate |
292 // both of those at the same time if it's useful. | 309 // both of those at the same time if it's useful. |
293 thread.priority_class = NORMAL_PRIORITY_CLASS; | 310 thread.priority_class = NORMAL_PRIORITY_CLASS; |
294 | 311 |
295 thread.priority = thread_info.Priority; | 312 thread.priority = thread_info.Priority; |
296 | 313 |
297 process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info; | 314 process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info; |
298 NTSTATUS status = crashpad::NtQueryInformationThread( | 315 NTSTATUS status = crashpad::NtQueryInformationThread( |
299 thread_handle.get(), | 316 thread_handle.get(), |
300 static_cast<THREADINFOCLASS>(ThreadBasicInformation), | 317 static_cast<THREADINFOCLASS>(ThreadBasicInformation), |
301 &thread_basic_info, | 318 &thread_basic_info, |
302 sizeof(thread_basic_info), | 319 sizeof(thread_basic_info), |
303 nullptr); | 320 nullptr); |
304 if (!NT_SUCCESS(status)) { | 321 if (!NT_SUCCESS(status)) { |
305 NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread"; | 322 NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread"; |
306 continue; | 323 continue; |
307 } | 324 } |
308 | 325 |
309 // Read the TIB (Thread Information Block) which is the first element of the | 326 // Read the TIB (Thread Information Block) which is the first element of the |
310 // TEB, for its stack fields. | 327 // TEB, for its stack fields. |
311 process_types::NT_TIB<Traits> tib; | 328 process_types::NT_TIB<Traits> tib; |
312 if (ReadMemory(thread_basic_info.TebBaseAddress, sizeof(tib), &tib)) { | 329 thread.teb = thread_basic_info.TebBaseAddress; |
330 if (ReadMemory(thread.teb, sizeof(tib), &tib)) { | |
331 WinVMAddress base = 0; | |
332 WinVMAddress limit = 0; | |
333 // If we're reading a WOW64 process, then the TIB we just retrieved is the | |
334 // x64 one. The first word of the x64 TIB points at the x86 TIB. See | |
335 // https://msdn.microsoft.com/en-us/library/dn424783.aspx | |
336 if (is_wow64) { | |
337 process_types::NT_TIB<process_types::internal::Traits32> tib32; | |
338 thread.teb = tib.Wow64Teb; | |
339 if (ReadMemory(thread.teb, sizeof(tib32), &tib32)) { | |
340 base = tib32.StackBase; | |
341 limit = tib32.StackLimit; | |
342 } | |
343 } else { | |
344 base = tib.StackBase; | |
345 limit = tib.StackLimit; | |
346 } | |
347 | |
313 // Note, "backwards" because of direction of stack growth. | 348 // Note, "backwards" because of direction of stack growth. |
314 thread.stack_region_address = tib.StackLimit; | 349 thread.stack_region_address = limit; |
315 if (tib.StackLimit > tib.StackBase) { | 350 if (limit > base) { |
316 LOG(ERROR) << "invalid stack range: " << tib.StackBase << " - " | 351 LOG(ERROR) << "invalid stack range: " << base << " - " << limit; |
317 << tib.StackLimit; | |
318 thread.stack_region_size = 0; | 352 thread.stack_region_size = 0; |
319 } else { | 353 } else { |
320 thread.stack_region_size = tib.StackBase - tib.StackLimit; | 354 thread.stack_region_size = base - limit; |
321 } | 355 } |
322 } | 356 } |
323 threads_.push_back(thread); | 357 threads_.push_back(thread); |
324 } | 358 } |
325 } | 359 } |
326 | 360 |
327 } // namespace crashpad | 361 } // namespace crashpad |
OLD | NEW |