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..81a86704c177e7e7d2c64fdf19788bea3baa5818 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,8 @@ 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; |
status = crashpad::NtQuerySystemInformation( |
- static_cast<SYSTEM_INFORMATION_CLASS>( |
- kSystemExtendedProcessInformation), |
+ SystemProcessInformation, |
reinterpret_cast<void*>(buffer->get()), |
buffer_size, |
&buffer_size); |
@@ -77,7 +76,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 +94,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 +114,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) { |
+bool 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,10 +130,10 @@ 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; |
+ return false; |
} |
DCHECK(previous_suspend_count > 0 || |
suspension_state == ProcessSuspensionState::kRunning); |
@@ -147,15 +143,18 @@ 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)) { |
Mark Mentovai
2015/09/18 03:30:51
I was looking at the documentation for GetThreadCo
scottmg
2015/09/18 03:50:09
2/3rds of the way through the comment sludge... :)
|
PLOG(ERROR) << "GetThreadContext failed"; |
- return; |
+ return false; |
} |
- if (!ResumeThread(thread_handle.get())) { |
+ if (!ResumeThread(thread_handle)) { |
PLOG(ERROR) << "ResumeThread failed"; |
+ return false; |
} |
} |
+ |
+ return true; |
} |
} // namespace |
@@ -198,7 +197,7 @@ bool ProcessReaderWin::Initialize(HANDLE process, |
bool ProcessReaderWin::ReadMemory(WinVMAddress at, |
WinVMSize num_bytes, |
- void* into) { |
+ void* into) const { |
SIZE_T bytes_read; |
if (!ReadProcessMemory(process_, |
reinterpret_cast<void*>(at), |
@@ -243,26 +242,48 @@ const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { |
initialized_threads_ = true; |
+ if (process_info_.Is64Bit()) |
+ ReadThreadData<process_types::internal::Traits64>(); |
+ else |
+ ReadThreadData<process_types::internal::Traits32>(); |
+ |
+ return threads_; |
+} |
+ |
+const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() { |
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
+ |
+ if (!process_info_.Modules(&modules_)) { |
+ LOG(ERROR) << "couldn't retrieve modules"; |
+ } |
+ |
+ return modules_; |
+} |
+ |
+template <class Traits> |
+void ProcessReaderWin::ReadThreadData() { |
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); |
+ process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information = |
+ GetProcessInformation<Traits>(process_, &buffer); |
if (!process_information) |
- return threads_; |
+ return; |
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; |
+ const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info = |
+ process_information->Threads[i]; |
+ ProcessReaderWin::Thread thread; |
thread.id = thread_info.ClientId.UniqueThread; |
- FillThreadContextAndSuspendCount(thread_info, &thread, suspension_state_); |
+ ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); |
+ if (!thread_handle.is_valid()) |
+ continue; |
+ |
+ if (!FillThreadContextAndSuspendCount<Traits>( |
+ thread_handle.get(), &thread, suspension_state_)) { |
+ continue; |
+ } |
// TODO(scottmg): I believe we could reverse engineer the PriorityClass from |
// the Priority, BasePriority, and |
@@ -272,33 +293,35 @@ const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { |
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)) { |
+ |
+ 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 (ReadMemory(thread_basic_info.TebBaseAddress, 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; |
+ if (tib.StackLimit > tib.StackBase) { |
+ LOG(ERROR) << "invalid stack range: " << tib.StackBase << " - " |
+ << tib.StackLimit; |
+ thread.stack_region_size = 0; |
+ } else { |
+ thread.stack_region_size = tib.StackBase - tib.StackLimit; |
+ } |
} |
threads_.push_back(thread); |
} |
- |
- return threads_; |
-} |
- |
-const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() { |
- INITIALIZATION_STATE_DCHECK_VALID(initialized_); |
- |
- if (!process_info_.Modules(&modules_)) { |
- LOG(ERROR) << "couldn't retrieve modules"; |
- } |
- |
- return modules_; |
} |
} // namespace crashpad |