| Index: snapshot/win/process_reader_win.cc
|
| diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc
|
| index a8774b01d30af366c80ca61d80dc911a4acf3c8c..09e04594fa3d06588d1980766cb1ed154437b812 100644
|
| --- a/snapshot/win/process_reader_win.cc
|
| +++ b/snapshot/win/process_reader_win.cc
|
| @@ -64,22 +64,6 @@ NTSTATUS NtOpenThread(PHANDLE thread_handle,
|
| static_cast<const void*>(client_id));
|
| }
|
|
|
| -NTSTATUS NtQueryInformationThread(HANDLE thread_handle,
|
| - THREADINFOCLASS thread_information_class,
|
| - PVOID thread_information,
|
| - ULONG thread_information_length,
|
| - PULONG return_length) {
|
| - static decltype(::NtQueryInformationThread)* nt_query_information_thread =
|
| - reinterpret_cast<decltype(::NtQueryInformationThread)*>(GetProcAddress(
|
| - LoadLibrary(L"ntdll.dll"), "NtQueryInformationThread"));
|
| - DCHECK(nt_query_information_thread);
|
| - return nt_query_information_thread(thread_handle,
|
| - thread_information_class,
|
| - thread_information,
|
| - thread_information_length,
|
| - return_length);
|
| -}
|
| -
|
| // Copied from ntstatus.h because um/winnt.h conflicts with general inclusion of
|
| // ntstatus.h.
|
| #define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
|
| @@ -153,63 +137,69 @@ process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation(
|
| }
|
|
|
| template <class Traits>
|
| -uint32_t GetThreadSuspendCount(
|
| - const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<Traits>&
|
| - thread_info) {
|
| - // Wait reason values are from KWAIT_REASON in wdm.h. We don't need all of
|
| - // them, so just declare the one we need.
|
| - const ULONG kWaitReasonSuspended = 5;
|
| -
|
| - // Kernel mode enumerations for thread state come from
|
| - // http://www.nirsoft.net/kernel_struct/vista/KTHREAD_STATE.html and
|
| - // https://msdn.microsoft.com/en-us/library/system.diagnostics.threadstate(v=vs.110).aspx
|
| - const ULONG kThreadStateWaiting = 5;
|
| - const ULONG kThreadStateGateWait = 8;
|
| -
|
| - bool suspended = (thread_info.ThreadState == kThreadStateWaiting ||
|
| - thread_info.ThreadState == kThreadStateGateWait) &&
|
| - thread_info.WaitReason == kWaitReasonSuspended;
|
| - if (!suspended)
|
| - return 0;
|
| -
|
| - HANDLE thread_handle;
|
| - ACCESS_MASK query_access = THREAD_QUERY_LIMITED_INFORMATION;
|
| +HANDLE OpenThread(const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<
|
| + Traits>& thread_info) {
|
| + HANDLE handle;
|
| + ACCESS_MASK query_access = THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME;
|
| OBJECT_ATTRIBUTES object_attributes;
|
| InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr);
|
| NTSTATUS status = crashpad::NtOpenThread(
|
| - &thread_handle, query_access, &object_attributes, &thread_info.ClientId);
|
| + &handle, query_access, &object_attributes, &thread_info.ClientId);
|
| if (!NT_SUCCESS(status)) {
|
| - LOG(WARNING) << "couldn't open thread to retrieve suspend count";
|
| - // Fall back to something semi-reasonable. We know we're suspended at this
|
| - // point, so just return 1.
|
| - return 1;
|
| + LOG(ERROR) << "NtOpenThread failed";
|
| + return nullptr;
|
| }
|
| + return handle;
|
| +}
|
|
|
| - // Take ownership of this handle so we close on exit. NtClose and CloseHandle
|
| - // are identical.
|
| - ScopedKernelHANDLE handle(thread_handle);
|
| -
|
| - // From ntddk.h. winternl.h defines THREADINFOCLASS, but only one value.
|
| - const int kThreadSuspendCount = 35;
|
| - ULONG suspend_count;
|
| - status = crashpad::NtQueryInformationThread(
|
| - handle.get(),
|
| - static_cast<THREADINFOCLASS>(kThreadSuspendCount),
|
| - &suspend_count,
|
| - sizeof(suspend_count),
|
| - nullptr);
|
| - if (!NT_SUCCESS(status)) {
|
| - LOG(WARNING) << "NtQueryInformationThread failed" << std::hex << status;
|
| - return 1;
|
| - }
|
| +// It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a
|
| +// 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) {
|
| +
|
| + // 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 ==
|
| + 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) {
|
| + thread->suspend_count = 0;
|
| + RtlCaptureContext(&thread->context);
|
| + } else {
|
| + DWORD previous_suspend_count = SuspendThread(thread_handle.get());
|
| + if (previous_suspend_count == -1) {
|
| + PLOG(ERROR) << "SuspendThread failed";
|
| + return;
|
| + }
|
| + thread->suspend_count = previous_suspend_count;
|
| +
|
| + memset(&thread->context, 0, sizeof(thread->context));
|
| + thread->context.ContextFlags = CONTEXT_ALL;
|
| + if (!GetThreadContext(thread_handle.get(), &thread->context)) {
|
| + PLOG(ERROR) << "GetThreadContext failed";
|
| + return;
|
| + }
|
|
|
| - return suspend_count;
|
| + if (!ResumeThread(thread_handle.get())) {
|
| + PLOG(ERROR) << "ResumeThread failed";
|
| + }
|
| + }
|
| }
|
|
|
| } // namespace
|
|
|
| ProcessReaderWin::Thread::Thread()
|
| - : id(0),
|
| + : context(),
|
| + id(0),
|
| teb(0),
|
| stack_region_address(0),
|
| stack_region_size(0),
|
| @@ -250,7 +240,8 @@ bool ProcessReaderWin::ReadMemory(WinVMAddress at,
|
| base::checked_cast<SIZE_T>(num_bytes),
|
| &bytes_read) ||
|
| num_bytes != bytes_read) {
|
| - PLOG(ERROR) << "ReadMemory at " << at << " of " << num_bytes << " failed";
|
| + PLOG(ERROR) << "ReadMemory at 0x" << std::hex << at << std::dec << " of "
|
| + << num_bytes << " bytes failed";
|
| return false;
|
| }
|
| return true;
|
| @@ -304,7 +295,8 @@ const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() {
|
| thread_info = process_information->Threads[i];
|
| Thread thread;
|
| thread.id = thread_info.ClientId.UniqueThread;
|
| - thread.suspend_count = GetThreadSuspendCount(thread_info);
|
| +
|
| + FillThreadContextAndSuspendCount(thread_info, &thread);
|
|
|
| // TODO(scottmg): I believe we could reverse engineer the PriorityClass from
|
| // the Priority, BasePriority, and
|
| @@ -323,8 +315,8 @@ const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() {
|
| // its stack fields.
|
| process_types::NT_TIB<SizeTraits> tib;
|
| if (ReadMemory(thread_info.TebBase, sizeof(tib), &tib)) {
|
| - thread.stack_region_address = tib.StackBase;
|
| // 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);
|
|
|