Chromium Code Reviews| Index: snapshot/win/process_reader_win.cc |
| diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc |
| index bad81cd7f98311135395f61ddf0c14780b14027e..fb57ce243ae55b9187d43e0d09b63f6a7095d0cf 100644 |
| --- a/snapshot/win/process_reader_win.cc |
| +++ b/snapshot/win/process_reader_win.cc |
| @@ -19,6 +19,7 @@ |
| #include "base/memory/scoped_ptr.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "util/win/nt_internals.h" |
| +#include "util/win/ntstatus_logging.h" |
| #include "util/win/process_structs.h" |
| #include "util/win/scoped_handle.h" |
| #include "util/win/time.h" |
| @@ -57,10 +58,9 @@ process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation( |
| // This must be in retry loop, as we're racing with process creation on the |
| // system to find a buffer large enough to hold all process information. |
| for (int tries = 0; tries < 20; ++tries) { |
| - const int kSystemExtendedProcessInformation = 57; |
| + const int kSystemProcessInformation = 5; |
|
Mark Mentovai
2015/09/16 02:57:19
Before this, kSystemExtendedProcessInformation had
scottmg
2015/09/16 18:05:06
Aha, good catch, done.
|
| status = crashpad::NtQuerySystemInformation( |
| - static_cast<SYSTEM_INFORMATION_CLASS>( |
| - kSystemExtendedProcessInformation), |
| + static_cast<SYSTEM_INFORMATION_CLASS>(kSystemProcessInformation), |
|
Mark Mentovai
2015/09/16 02:57:19
And then you won’t need a cast anymore!
scottmg
2015/09/16 18:05:06
Done.
|
| reinterpret_cast<void*>(buffer->get()), |
| buffer_size, |
| &buffer_size); |
| @@ -77,7 +77,7 @@ process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation( |
| } |
| if (!NT_SUCCESS(status)) { |
| - LOG(ERROR) << "NtQuerySystemInformation failed: " << std::hex << status; |
| + NTSTATUS_LOG(ERROR, status) << "NtQuerySystemInformation"; |
| return nullptr; |
| } |
| @@ -95,16 +95,17 @@ process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation( |
| } |
| template <class Traits> |
| -HANDLE OpenThread(const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION< |
| - Traits>& thread_info) { |
| +HANDLE OpenThread( |
| + const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info) { |
| HANDLE handle; |
| - ACCESS_MASK query_access = THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME; |
| + ACCESS_MASK query_access = |
| + THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION; |
| OBJECT_ATTRIBUTES object_attributes; |
| InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr); |
| NTSTATUS status = crashpad::NtOpenThread( |
| &handle, query_access, &object_attributes, &thread_info.ClientId); |
| if (!NT_SUCCESS(status)) { |
| - LOG(ERROR) << "NtOpenThread failed"; |
| + NTSTATUS_LOG(ERROR, status) << "NtOpenThread"; |
| return nullptr; |
| } |
| return handle; |
| @@ -114,19 +115,15 @@ HANDLE OpenThread(const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION< |
| // side-effect of returning the SuspendCount of the thread on success, so we |
| // fill out these two pieces of semi-unrelated data in the same function. |
| template <class Traits> |
| -void FillThreadContextAndSuspendCount( |
| - const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<Traits>& |
| - thread_info, |
| - ProcessReaderWin::Thread* thread, |
| - ProcessSuspensionState suspension_state) { |
| +void FillThreadContextAndSuspendCount(HANDLE thread_handle, |
| + ProcessReaderWin::Thread* thread, |
| + ProcessSuspensionState suspension_state) { |
| // Don't suspend the thread if it's this thread. This is really only for test |
| // binaries, as we won't be walking ourselves, in general. |
| - bool is_current_thread = thread_info.ClientId.UniqueThread == |
| + bool is_current_thread = thread->id == |
| reinterpret_cast<process_types::TEB<Traits>*>( |
| NtCurrentTeb())->ClientId.UniqueThread; |
| - ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); |
| - |
| // TODO(scottmg): Handle cross-bitness in this function. |
| if (is_current_thread) { |
| @@ -134,7 +131,7 @@ void FillThreadContextAndSuspendCount( |
| thread->suspend_count = 0; |
| RtlCaptureContext(&thread->context); |
| } else { |
| - DWORD previous_suspend_count = SuspendThread(thread_handle.get()); |
| + DWORD previous_suspend_count = SuspendThread(thread_handle); |
| if (previous_suspend_count == -1) { |
| PLOG(ERROR) << "SuspendThread failed"; |
| return; |
| @@ -147,12 +144,12 @@ void FillThreadContextAndSuspendCount( |
| memset(&thread->context, 0, sizeof(thread->context)); |
| thread->context.ContextFlags = CONTEXT_ALL; |
| - if (!GetThreadContext(thread_handle.get(), &thread->context)) { |
| + if (!GetThreadContext(thread_handle, &thread->context)) { |
| PLOG(ERROR) << "GetThreadContext failed"; |
| return; |
| } |
| - if (!ResumeThread(thread_handle.get())) { |
| + if (!ResumeThread(thread_handle)) { |
| PLOG(ERROR) << "ResumeThread failed"; |
| } |
| } |
| @@ -160,6 +157,63 @@ void FillThreadContextAndSuspendCount( |
| } // namespace |
| +template <class Traits> |
|
scottmg
2015/09/15 21:13:34
On XP, the "extended" version of this structure do
|
| +void ReadThreadData(ProcessReaderWin* process_reader) { |
|
Mark Mentovai
2015/09/16 02:57:19
This belongs the unnamed namespace.
scottmg
2015/09/16 18:05:06
It wasn't because it needed a friend declaration i
|
| + DCHECK(process_reader->threads_.empty()); |
| + |
| + scoped_ptr<uint8_t[]> buffer; |
| + process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information = |
| + GetProcessInformation<Traits>(process_reader->process_, &buffer); |
| + if (!process_information) |
| + return; |
| + |
| + for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { |
| + const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info = |
| + process_information->Threads[i]; |
| + ProcessReaderWin::Thread thread; |
| + thread.id = thread_info.ClientId.UniqueThread; |
| + |
| + ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); |
| + if (!thread_handle.is_valid()) |
| + continue; |
| + |
| + FillThreadContextAndSuspendCount<Traits>( |
|
Mark Mentovai
2015/09/16 02:57:19
(This and the next comment describe things found i
scottmg
2015/09/16 18:05:06
Done.
|
| + thread_handle.get(), &thread, process_reader->suspension_state_); |
| + |
| + // TODO(scottmg): I believe we could reverse engineer the PriorityClass from |
| + // the Priority, BasePriority, and |
| + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 . |
| + // MinidumpThreadWriter doesn't handle it yet in any case, so investigate |
| + // both of those at the same time if it's useful. |
| + thread.priority_class = NORMAL_PRIORITY_CLASS; |
| + |
| + thread.priority = thread_info.Priority; |
| + |
| + process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info; |
| + NTSTATUS status = crashpad::NtQueryInformationThread( |
| + thread_handle.get(), |
| + static_cast<THREADINFOCLASS>(ThreadBasicInformation), |
| + &thread_basic_info, |
| + sizeof(thread_basic_info), |
| + nullptr); |
| + if (!NT_SUCCESS(status)) { |
| + NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread"; |
| + continue; |
| + } |
| + |
| + // Read the TIB (Thread Information Block) which is the first element of the |
| + // TEB, for its stack fields. |
| + process_types::NT_TIB<Traits> tib; |
| + if (process_reader->ReadMemory( |
| + thread_basic_info.TebBaseAddress, sizeof(tib), &tib)) { |
| + // Note, "backwards" because of direction of stack growth. |
|
Mark Mentovai
2015/09/16 02:57:19
Might be good to actually check that limit <= base
scottmg
2015/09/16 18:05:06
Done.
|
| + thread.stack_region_address = tib.StackLimit; |
| + thread.stack_region_size = tib.StackBase - tib.StackLimit; |
| + } |
| + process_reader->threads_.push_back(thread); |
| + } |
| +} |
| + |
| ProcessReaderWin::Thread::Thread() |
| : context(), |
| id(0), |
| @@ -243,50 +297,10 @@ const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { |
| initialized_threads_ = true; |
| - DCHECK(threads_.empty()); |
| - |
| -#if ARCH_CPU_32_BITS |
| - using SizeTraits = process_types::internal::Traits32; |
| -#else |
| - using SizeTraits = process_types::internal::Traits64; |
| -#endif |
| - scoped_ptr<uint8_t[]> buffer; |
| - process_types::SYSTEM_PROCESS_INFORMATION<SizeTraits>* process_information = |
| - GetProcessInformation<SizeTraits>(process_, &buffer); |
| - if (!process_information) |
| - return threads_; |
| - |
| - for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { |
| - const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<SizeTraits>& |
| - thread_info = process_information->Threads[i]; |
| - Thread thread; |
| - thread.id = thread_info.ClientId.UniqueThread; |
| - |
| - FillThreadContextAndSuspendCount(thread_info, &thread, suspension_state_); |
| - |
| - // TODO(scottmg): I believe we could reverse engineer the PriorityClass from |
| - // the Priority, BasePriority, and |
| - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 . |
| - // MinidumpThreadWriter doesn't handle it yet in any case, so investigate |
| - // both of those at the same time if it's useful. |
| - thread.priority_class = NORMAL_PRIORITY_CLASS; |
| - |
| - thread.priority = thread_info.Priority; |
| - thread.teb = thread_info.TebBase; |
| - |
| - // While there are semi-documented fields in the thread structure called |
| - // StackBase and StackLimit, they don't appear to be correct in practice (or |
| - // at least, I don't know how to interpret them). Instead, read the TIB |
| - // (Thread Information Block) which is the first element of the TEB, and use |
| - // its stack fields. |
| - process_types::NT_TIB<SizeTraits> tib; |
| - if (ReadMemory(thread_info.TebBase, sizeof(tib), &tib)) { |
| - // Note, "backwards" because of direction of stack growth. |
| - thread.stack_region_address = tib.StackLimit; |
| - thread.stack_region_size = tib.StackBase - tib.StackLimit; |
| - } |
| - threads_.push_back(thread); |
| - } |
| + if (process_info_.Is64Bit()) |
| + ReadThreadData<process_types::internal::Traits64>(this); |
| + else |
| + ReadThreadData<process_types::internal::Traits32>(this); |
| return threads_; |
| } |