Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(786)

Side by Side Diff: snapshot/win/process_reader_win.cc

Issue 1336823002: win x86: Grab bag of restructuring to get tests working on x86-on-x86 (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: fixes2 Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and 12 // See the License for the specific language governing permissions and
13 // limitations under the License. 13 // limitations under the License.
14 14
15 #include "snapshot/win/process_reader_win.h" 15 #include "snapshot/win/process_reader_win.h"
16 16
17 #include <winternl.h> 17 #include <winternl.h>
18 18
19 #include "base/memory/scoped_ptr.h" 19 #include "base/memory/scoped_ptr.h"
20 #include "base/numerics/safe_conversions.h" 20 #include "base/numerics/safe_conversions.h"
21 #include "util/win/nt_internals.h" 21 #include "util/win/nt_internals.h"
22 #include "util/win/ntstatus_logging.h"
22 #include "util/win/process_structs.h" 23 #include "util/win/process_structs.h"
23 #include "util/win/scoped_handle.h" 24 #include "util/win/scoped_handle.h"
24 #include "util/win/time.h" 25 #include "util/win/time.h"
25 26
26 namespace crashpad { 27 namespace crashpad {
27 28
28 namespace { 29 namespace {
29 30
30 // Gets a pointer to the process information structure after a given one, or 31 // Gets a pointer to the process information structure after a given one, or
31 // null when iteration is complete, assuming they've been retrieved in a block 32 // null when iteration is complete, assuming they've been retrieved in a block
(...skipping 18 matching lines...) Expand all
50 template <class Traits> 51 template <class Traits>
51 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation( 52 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* GetProcessInformation(
52 HANDLE process_handle, 53 HANDLE process_handle,
53 scoped_ptr<uint8_t[]>* buffer) { 54 scoped_ptr<uint8_t[]>* buffer) {
54 ULONG buffer_size = 16384; 55 ULONG buffer_size = 16384;
55 buffer->reset(new uint8_t[buffer_size]); 56 buffer->reset(new uint8_t[buffer_size]);
56 NTSTATUS status; 57 NTSTATUS status;
57 // This must be in retry loop, as we're racing with process creation on the 58 // This must be in retry loop, as we're racing with process creation on the
58 // system to find a buffer large enough to hold all process information. 59 // system to find a buffer large enough to hold all process information.
59 for (int tries = 0; tries < 20; ++tries) { 60 for (int tries = 0; tries < 20; ++tries) {
60 const int kSystemExtendedProcessInformation = 57;
61 status = crashpad::NtQuerySystemInformation( 61 status = crashpad::NtQuerySystemInformation(
62 static_cast<SYSTEM_INFORMATION_CLASS>( 62 SystemProcessInformation,
63 kSystemExtendedProcessInformation),
64 reinterpret_cast<void*>(buffer->get()), 63 reinterpret_cast<void*>(buffer->get()),
65 buffer_size, 64 buffer_size,
66 &buffer_size); 65 &buffer_size);
67 if (status == STATUS_BUFFER_TOO_SMALL || 66 if (status == STATUS_BUFFER_TOO_SMALL ||
68 status == STATUS_INFO_LENGTH_MISMATCH) { 67 status == STATUS_INFO_LENGTH_MISMATCH) {
69 // Add a little extra to try to avoid an additional loop iteration. We're 68 // Add a little extra to try to avoid an additional loop iteration. We're
70 // racing with system-wide process creation between here and the next call 69 // racing with system-wide process creation between here and the next call
71 // to NtQuerySystemInformation(). 70 // to NtQuerySystemInformation().
72 buffer_size += 4096; 71 buffer_size += 4096;
73 buffer->reset(new uint8_t[buffer_size]); 72 buffer->reset(new uint8_t[buffer_size]);
74 } else { 73 } else {
75 break; 74 break;
76 } 75 }
77 } 76 }
78 77
79 if (!NT_SUCCESS(status)) { 78 if (!NT_SUCCESS(status)) {
80 LOG(ERROR) << "NtQuerySystemInformation failed: " << std::hex << status; 79 NTSTATUS_LOG(ERROR, status) << "NtQuerySystemInformation";
81 return nullptr; 80 return nullptr;
82 } 81 }
83 82
84 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process = 83 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process =
85 reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>( 84 reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
86 buffer->get()); 85 buffer->get());
87 DWORD process_id = GetProcessId(process_handle); 86 DWORD process_id = GetProcessId(process_handle);
88 do { 87 do {
89 if (process->UniqueProcessId == process_id) 88 if (process->UniqueProcessId == process_id)
90 return process; 89 return process;
91 } while (process = NextProcess(process)); 90 } while (process = NextProcess(process));
92 91
93 LOG(ERROR) << "process " << process_id << " not found"; 92 LOG(ERROR) << "process " << process_id << " not found";
94 return nullptr; 93 return nullptr;
95 } 94 }
96 95
97 template <class Traits> 96 template <class Traits>
98 HANDLE OpenThread(const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION< 97 HANDLE OpenThread(
99 Traits>& thread_info) { 98 const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info) {
100 HANDLE handle; 99 HANDLE handle;
101 ACCESS_MASK query_access = THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME; 100 ACCESS_MASK query_access =
101 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION;
102 OBJECT_ATTRIBUTES object_attributes; 102 OBJECT_ATTRIBUTES object_attributes;
103 InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr); 103 InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr);
104 NTSTATUS status = crashpad::NtOpenThread( 104 NTSTATUS status = crashpad::NtOpenThread(
105 &handle, query_access, &object_attributes, &thread_info.ClientId); 105 &handle, query_access, &object_attributes, &thread_info.ClientId);
106 if (!NT_SUCCESS(status)) { 106 if (!NT_SUCCESS(status)) {
107 LOG(ERROR) << "NtOpenThread failed"; 107 NTSTATUS_LOG(ERROR, status) << "NtOpenThread";
108 return nullptr; 108 return nullptr;
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 void FillThreadContextAndSuspendCount( 117 bool FillThreadContextAndSuspendCount(HANDLE thread_handle,
118 const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<Traits>& 118 ProcessReaderWin::Thread* thread,
119 thread_info, 119 ProcessSuspensionState suspension_state) {
120 ProcessReaderWin::Thread* thread,
121 ProcessSuspensionState suspension_state) {
122 // Don't suspend the thread if it's this thread. This is really only for test 120 // Don't suspend the thread if it's this thread. This is really only for test
123 // binaries, as we won't be walking ourselves, in general. 121 // binaries, as we won't be walking ourselves, in general.
124 bool is_current_thread = thread_info.ClientId.UniqueThread == 122 bool is_current_thread = thread->id ==
125 reinterpret_cast<process_types::TEB<Traits>*>( 123 reinterpret_cast<process_types::TEB<Traits>*>(
126 NtCurrentTeb())->ClientId.UniqueThread; 124 NtCurrentTeb())->ClientId.UniqueThread;
127 125
128 ScopedKernelHANDLE thread_handle(OpenThread(thread_info));
129
130 // TODO(scottmg): Handle cross-bitness in this function. 126 // TODO(scottmg): Handle cross-bitness in this function.
131 127
132 if (is_current_thread) { 128 if (is_current_thread) {
133 DCHECK(suspension_state == ProcessSuspensionState::kRunning); 129 DCHECK(suspension_state == ProcessSuspensionState::kRunning);
134 thread->suspend_count = 0; 130 thread->suspend_count = 0;
135 RtlCaptureContext(&thread->context); 131 RtlCaptureContext(&thread->context);
136 } else { 132 } else {
137 DWORD previous_suspend_count = SuspendThread(thread_handle.get()); 133 DWORD previous_suspend_count = SuspendThread(thread_handle);
138 if (previous_suspend_count == -1) { 134 if (previous_suspend_count == -1) {
139 PLOG(ERROR) << "SuspendThread failed"; 135 PLOG(ERROR) << "SuspendThread failed";
140 return; 136 return false;
141 } 137 }
142 DCHECK(previous_suspend_count > 0 || 138 DCHECK(previous_suspend_count > 0 ||
143 suspension_state == ProcessSuspensionState::kRunning); 139 suspension_state == ProcessSuspensionState::kRunning);
144 thread->suspend_count = 140 thread->suspend_count =
145 previous_suspend_count - 141 previous_suspend_count -
146 (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0); 142 (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0);
147 143
148 memset(&thread->context, 0, sizeof(thread->context)); 144 memset(&thread->context, 0, sizeof(thread->context));
149 thread->context.ContextFlags = CONTEXT_ALL; 145 thread->context.ContextFlags = CONTEXT_ALL;
150 if (!GetThreadContext(thread_handle.get(), &thread->context)) { 146 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... :)
151 PLOG(ERROR) << "GetThreadContext failed"; 147 PLOG(ERROR) << "GetThreadContext failed";
152 return; 148 return false;
153 } 149 }
154 150
155 if (!ResumeThread(thread_handle.get())) { 151 if (!ResumeThread(thread_handle)) {
156 PLOG(ERROR) << "ResumeThread failed"; 152 PLOG(ERROR) << "ResumeThread failed";
153 return false;
157 } 154 }
158 } 155 }
156
157 return true;
159 } 158 }
160 159
161 } // namespace 160 } // namespace
162 161
163 ProcessReaderWin::Thread::Thread() 162 ProcessReaderWin::Thread::Thread()
164 : context(), 163 : context(),
165 id(0), 164 id(0),
166 teb(0), 165 teb(0),
167 stack_region_address(0), 166 stack_region_address(0),
168 stack_region_size(0), 167 stack_region_size(0),
(...skipping 22 matching lines...) Expand all
191 process_ = process; 190 process_ = process;
192 suspension_state_ = suspension_state; 191 suspension_state_ = suspension_state;
193 process_info_.Initialize(process); 192 process_info_.Initialize(process);
194 193
195 INITIALIZATION_STATE_SET_VALID(initialized_); 194 INITIALIZATION_STATE_SET_VALID(initialized_);
196 return true; 195 return true;
197 } 196 }
198 197
199 bool ProcessReaderWin::ReadMemory(WinVMAddress at, 198 bool ProcessReaderWin::ReadMemory(WinVMAddress at,
200 WinVMSize num_bytes, 199 WinVMSize num_bytes,
201 void* into) { 200 void* into) const {
202 SIZE_T bytes_read; 201 SIZE_T bytes_read;
203 if (!ReadProcessMemory(process_, 202 if (!ReadProcessMemory(process_,
204 reinterpret_cast<void*>(at), 203 reinterpret_cast<void*>(at),
205 into, 204 into,
206 base::checked_cast<SIZE_T>(num_bytes), 205 base::checked_cast<SIZE_T>(num_bytes),
207 &bytes_read) || 206 &bytes_read) ||
208 num_bytes != bytes_read) { 207 num_bytes != bytes_read) {
209 PLOG(ERROR) << "ReadMemory at 0x" << std::hex << at << std::dec << " of " 208 PLOG(ERROR) << "ReadMemory at 0x" << std::hex << at << std::dec << " of "
210 << num_bytes << " bytes failed"; 209 << num_bytes << " bytes failed";
211 return false; 210 return false;
(...skipping 24 matching lines...) Expand all
236 } 235 }
237 236
238 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { 237 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() {
239 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 238 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
240 239
241 if (initialized_threads_) 240 if (initialized_threads_)
242 return threads_; 241 return threads_;
243 242
244 initialized_threads_ = true; 243 initialized_threads_ = true;
245 244
245 if (process_info_.Is64Bit())
246 ReadThreadData<process_types::internal::Traits64>();
247 else
248 ReadThreadData<process_types::internal::Traits32>();
249
250 return threads_;
251 }
252
253 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() {
254 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
255
256 if (!process_info_.Modules(&modules_)) {
257 LOG(ERROR) << "couldn't retrieve modules";
258 }
259
260 return modules_;
261 }
262
263 template <class Traits>
264 void ProcessReaderWin::ReadThreadData() {
246 DCHECK(threads_.empty()); 265 DCHECK(threads_.empty());
247 266
248 #if ARCH_CPU_32_BITS
249 using SizeTraits = process_types::internal::Traits32;
250 #else
251 using SizeTraits = process_types::internal::Traits64;
252 #endif
253 scoped_ptr<uint8_t[]> buffer; 267 scoped_ptr<uint8_t[]> buffer;
254 process_types::SYSTEM_PROCESS_INFORMATION<SizeTraits>* process_information = 268 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information =
255 GetProcessInformation<SizeTraits>(process_, &buffer); 269 GetProcessInformation<Traits>(process_, &buffer);
256 if (!process_information) 270 if (!process_information)
257 return threads_; 271 return;
258 272
259 for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { 273 for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) {
260 const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<SizeTraits>& 274 const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info =
261 thread_info = process_information->Threads[i]; 275 process_information->Threads[i];
262 Thread thread; 276 ProcessReaderWin::Thread thread;
263 thread.id = thread_info.ClientId.UniqueThread; 277 thread.id = thread_info.ClientId.UniqueThread;
264 278
265 FillThreadContextAndSuspendCount(thread_info, &thread, suspension_state_); 279 ScopedKernelHANDLE thread_handle(OpenThread(thread_info));
280 if (!thread_handle.is_valid())
281 continue;
282
283 if (!FillThreadContextAndSuspendCount<Traits>(
284 thread_handle.get(), &thread, suspension_state_)) {
285 continue;
286 }
266 287
267 // TODO(scottmg): I believe we could reverse engineer the PriorityClass from 288 // TODO(scottmg): I believe we could reverse engineer the PriorityClass from
268 // the Priority, BasePriority, and 289 // the Priority, BasePriority, and
269 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 . 290 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 .
270 // MinidumpThreadWriter doesn't handle it yet in any case, so investigate 291 // MinidumpThreadWriter doesn't handle it yet in any case, so investigate
271 // both of those at the same time if it's useful. 292 // both of those at the same time if it's useful.
272 thread.priority_class = NORMAL_PRIORITY_CLASS; 293 thread.priority_class = NORMAL_PRIORITY_CLASS;
273 294
274 thread.priority = thread_info.Priority; 295 thread.priority = thread_info.Priority;
275 thread.teb = thread_info.TebBase;
276 296
277 // While there are semi-documented fields in the thread structure called 297 process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info;
278 // StackBase and StackLimit, they don't appear to be correct in practice (or 298 NTSTATUS status = crashpad::NtQueryInformationThread(
279 // at least, I don't know how to interpret them). Instead, read the TIB 299 thread_handle.get(),
280 // (Thread Information Block) which is the first element of the TEB, and use 300 static_cast<THREADINFOCLASS>(ThreadBasicInformation),
281 // its stack fields. 301 &thread_basic_info,
282 process_types::NT_TIB<SizeTraits> tib; 302 sizeof(thread_basic_info),
283 if (ReadMemory(thread_info.TebBase, sizeof(tib), &tib)) { 303 nullptr);
304 if (!NT_SUCCESS(status)) {
305 NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread";
306 continue;
307 }
308
309 // Read the TIB (Thread Information Block) which is the first element of the
310 // TEB, for its stack fields.
311 process_types::NT_TIB<Traits> tib;
312 if (ReadMemory(thread_basic_info.TebBaseAddress, sizeof(tib), &tib)) {
284 // Note, "backwards" because of direction of stack growth. 313 // Note, "backwards" because of direction of stack growth.
285 thread.stack_region_address = tib.StackLimit; 314 thread.stack_region_address = tib.StackLimit;
286 thread.stack_region_size = tib.StackBase - tib.StackLimit; 315 if (tib.StackLimit > tib.StackBase) {
316 LOG(ERROR) << "invalid stack range: " << tib.StackBase << " - "
317 << tib.StackLimit;
318 thread.stack_region_size = 0;
319 } else {
320 thread.stack_region_size = tib.StackBase - tib.StackLimit;
321 }
287 } 322 }
288 threads_.push_back(thread); 323 threads_.push_back(thread);
289 } 324 }
290
291 return threads_;
292 }
293
294 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() {
295 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
296
297 if (!process_info_.Modules(&modules_)) {
298 LOG(ERROR) << "couldn't retrieve modules";
299 }
300
301 return modules_;
302 } 325 }
303 326
304 } // namespace crashpad 327 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698