| 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_64_reading_32) { |
| 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_64_reading_32); |
| 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"; |
| 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 memset(&thread->context, 0, sizeof(thread->context)); |
| 145 thread->context.ContextFlags = CONTEXT_ALL; | 145 #if defined(ARCH_CPU_32_BITS) |
| 146 if (!GetThreadContext(thread_handle, &thread->context)) { | 146 const bool is_native = true; |
| 147 PLOG(ERROR) << "GetThreadContext failed"; | 147 #elif defined(ARCH_CPU_64_BITS) |
| 148 return false; | 148 const bool is_native = !is_64_reading_32; |
| 149 if (is_64_reading_32) { |
| 150 thread->context.wow64.ContextFlags = CONTEXT_ALL; |
| 151 if (!Wow64GetThreadContext(thread_handle, &thread->context.wow64)) { |
| 152 PLOG(ERROR) << "Wow64GetThreadContext"; |
| 153 return false; |
| 154 } |
| 155 } |
| 156 #endif |
| 157 if (is_native) { |
| 158 thread->context.native.ContextFlags = CONTEXT_ALL; |
| 159 if (!GetThreadContext(thread_handle, &thread->context.native)) { |
| 160 PLOG(ERROR) << "GetThreadContext"; |
| 161 return false; |
| 162 } |
| 149 } | 163 } |
| 150 | 164 |
| 151 if (!ResumeThread(thread_handle)) { | 165 if (!ResumeThread(thread_handle)) { |
| 152 PLOG(ERROR) << "ResumeThread failed"; | 166 PLOG(ERROR) << "ResumeThread"; |
| 153 return false; | 167 return false; |
| 154 } | 168 } |
| 155 } | 169 } |
| 156 | 170 |
| 157 return true; | 171 return true; |
| 158 } | 172 } |
| 159 | 173 |
| 160 } // namespace | 174 } // namespace |
| 161 | 175 |
| 162 ProcessReaderWin::Thread::Thread() | 176 ProcessReaderWin::Thread::Thread() |
| (...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 235 } | 249 } |
| 236 | 250 |
| 237 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { | 251 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { |
| 238 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 252 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 239 | 253 |
| 240 if (initialized_threads_) | 254 if (initialized_threads_) |
| 241 return threads_; | 255 return threads_; |
| 242 | 256 |
| 243 initialized_threads_ = true; | 257 initialized_threads_ = true; |
| 244 | 258 |
| 245 if (process_info_.Is64Bit()) | 259 #if defined(ARCH_CPU_64_BITS) |
| 246 ReadThreadData<process_types::internal::Traits64>(); | 260 ReadThreadData<process_types::internal::Traits64>(process_info_.IsWow64()); |
| 247 else | 261 #else |
| 248 ReadThreadData<process_types::internal::Traits32>(); | 262 ReadThreadData<process_types::internal::Traits32>(false); |
| 263 #endif |
| 249 | 264 |
| 250 return threads_; | 265 return threads_; |
| 251 } | 266 } |
| 252 | 267 |
| 253 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() { | 268 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() { |
| 254 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | 269 INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
| 255 | 270 |
| 256 if (!process_info_.Modules(&modules_)) { | 271 if (!process_info_.Modules(&modules_)) { |
| 257 LOG(ERROR) << "couldn't retrieve modules"; | 272 LOG(ERROR) << "couldn't retrieve modules"; |
| 258 } | 273 } |
| 259 | 274 |
| 260 return modules_; | 275 return modules_; |
| 261 } | 276 } |
| 262 | 277 |
| 263 template <class Traits> | 278 template <class Traits> |
| 264 void ProcessReaderWin::ReadThreadData() { | 279 void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) { |
| 265 DCHECK(threads_.empty()); | 280 DCHECK(threads_.empty()); |
| 266 | 281 |
| 267 scoped_ptr<uint8_t[]> buffer; | 282 scoped_ptr<uint8_t[]> buffer; |
| 268 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information = | 283 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information = |
| 269 GetProcessInformation<Traits>(process_, &buffer); | 284 GetProcessInformation<Traits>(process_, &buffer); |
| 270 if (!process_information) | 285 if (!process_information) |
| 271 return; | 286 return; |
| 272 | 287 |
| 273 for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { | 288 for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { |
| 274 const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info = | 289 const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info = |
| 275 process_information->Threads[i]; | 290 process_information->Threads[i]; |
| 276 ProcessReaderWin::Thread thread; | 291 ProcessReaderWin::Thread thread; |
| 277 thread.id = thread_info.ClientId.UniqueThread; | 292 thread.id = thread_info.ClientId.UniqueThread; |
| 278 | 293 |
| 279 ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); | 294 ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); |
| 280 if (!thread_handle.is_valid()) | 295 if (!thread_handle.is_valid()) |
| 281 continue; | 296 continue; |
| 282 | 297 |
| 283 if (!FillThreadContextAndSuspendCount<Traits>( | 298 if (!FillThreadContextAndSuspendCount<Traits>(thread_handle.get(), |
| 284 thread_handle.get(), &thread, suspension_state_)) { | 299 &thread, |
| 300 suspension_state_, |
| 301 is_64_reading_32)) { |
| 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_64_reading_32) { |
| 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 |