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

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: . 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 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.
61 status = crashpad::NtQuerySystemInformation( 62 status = crashpad::NtQuerySystemInformation(
62 static_cast<SYSTEM_INFORMATION_CLASS>( 63 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.
63 kSystemExtendedProcessInformation),
64 reinterpret_cast<void*>(buffer->get()), 64 reinterpret_cast<void*>(buffer->get()),
65 buffer_size, 65 buffer_size,
66 &buffer_size); 66 &buffer_size);
67 if (status == STATUS_BUFFER_TOO_SMALL || 67 if (status == STATUS_BUFFER_TOO_SMALL ||
68 status == STATUS_INFO_LENGTH_MISMATCH) { 68 status == STATUS_INFO_LENGTH_MISMATCH) {
69 // Add a little extra to try to avoid an additional loop iteration. We're 69 // 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 70 // racing with system-wide process creation between here and the next call
71 // to NtQuerySystemInformation(). 71 // to NtQuerySystemInformation().
72 buffer_size += 4096; 72 buffer_size += 4096;
73 buffer->reset(new uint8_t[buffer_size]); 73 buffer->reset(new uint8_t[buffer_size]);
74 } else { 74 } else {
75 break; 75 break;
76 } 76 }
77 } 77 }
78 78
79 if (!NT_SUCCESS(status)) { 79 if (!NT_SUCCESS(status)) {
80 LOG(ERROR) << "NtQuerySystemInformation failed: " << std::hex << status; 80 NTSTATUS_LOG(ERROR, status) << "NtQuerySystemInformation";
81 return nullptr; 81 return nullptr;
82 } 82 }
83 83
84 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process = 84 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process =
85 reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>( 85 reinterpret_cast<process_types::SYSTEM_PROCESS_INFORMATION<Traits>*>(
86 buffer->get()); 86 buffer->get());
87 DWORD process_id = GetProcessId(process_handle); 87 DWORD process_id = GetProcessId(process_handle);
88 do { 88 do {
89 if (process->UniqueProcessId == process_id) 89 if (process->UniqueProcessId == process_id)
90 return process; 90 return process;
91 } while (process = NextProcess(process)); 91 } while (process = NextProcess(process));
92 92
93 LOG(ERROR) << "process " << process_id << " not found"; 93 LOG(ERROR) << "process " << process_id << " not found";
94 return nullptr; 94 return nullptr;
95 } 95 }
96 96
97 template <class Traits> 97 template <class Traits>
98 HANDLE OpenThread(const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION< 98 HANDLE OpenThread(
99 Traits>& thread_info) { 99 const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info) {
100 HANDLE handle; 100 HANDLE handle;
101 ACCESS_MASK query_access = THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME; 101 ACCESS_MASK query_access =
102 THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION;
102 OBJECT_ATTRIBUTES object_attributes; 103 OBJECT_ATTRIBUTES object_attributes;
103 InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr); 104 InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr);
104 NTSTATUS status = crashpad::NtOpenThread( 105 NTSTATUS status = crashpad::NtOpenThread(
105 &handle, query_access, &object_attributes, &thread_info.ClientId); 106 &handle, query_access, &object_attributes, &thread_info.ClientId);
106 if (!NT_SUCCESS(status)) { 107 if (!NT_SUCCESS(status)) {
107 LOG(ERROR) << "NtOpenThread failed"; 108 NTSTATUS_LOG(ERROR, status) << "NtOpenThread";
108 return nullptr; 109 return nullptr;
109 } 110 }
110 return handle; 111 return handle;
111 } 112 }
112 113
113 // It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a 114 // 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 115 // 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. 116 // fill out these two pieces of semi-unrelated data in the same function.
116 template <class Traits> 117 template <class Traits>
117 void FillThreadContextAndSuspendCount( 118 void FillThreadContextAndSuspendCount(HANDLE thread_handle,
118 const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<Traits>& 119 ProcessReaderWin::Thread* thread,
119 thread_info, 120 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 121 // 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. 122 // binaries, as we won't be walking ourselves, in general.
124 bool is_current_thread = thread_info.ClientId.UniqueThread == 123 bool is_current_thread = thread->id ==
125 reinterpret_cast<process_types::TEB<Traits>*>( 124 reinterpret_cast<process_types::TEB<Traits>*>(
126 NtCurrentTeb())->ClientId.UniqueThread; 125 NtCurrentTeb())->ClientId.UniqueThread;
127 126
128 ScopedKernelHANDLE thread_handle(OpenThread(thread_info));
129
130 // TODO(scottmg): Handle cross-bitness in this function. 127 // TODO(scottmg): Handle cross-bitness in this function.
131 128
132 if (is_current_thread) { 129 if (is_current_thread) {
133 DCHECK(suspension_state == ProcessSuspensionState::kRunning); 130 DCHECK(suspension_state == ProcessSuspensionState::kRunning);
134 thread->suspend_count = 0; 131 thread->suspend_count = 0;
135 RtlCaptureContext(&thread->context); 132 RtlCaptureContext(&thread->context);
136 } else { 133 } else {
137 DWORD previous_suspend_count = SuspendThread(thread_handle.get()); 134 DWORD previous_suspend_count = SuspendThread(thread_handle);
138 if (previous_suspend_count == -1) { 135 if (previous_suspend_count == -1) {
139 PLOG(ERROR) << "SuspendThread failed"; 136 PLOG(ERROR) << "SuspendThread failed";
140 return; 137 return;
141 } 138 }
142 DCHECK(previous_suspend_count > 0 || 139 DCHECK(previous_suspend_count > 0 ||
143 suspension_state == ProcessSuspensionState::kRunning); 140 suspension_state == ProcessSuspensionState::kRunning);
144 thread->suspend_count = 141 thread->suspend_count =
145 previous_suspend_count - 142 previous_suspend_count -
146 (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0); 143 (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0);
147 144
148 memset(&thread->context, 0, sizeof(thread->context)); 145 memset(&thread->context, 0, sizeof(thread->context));
149 thread->context.ContextFlags = CONTEXT_ALL; 146 thread->context.ContextFlags = CONTEXT_ALL;
150 if (!GetThreadContext(thread_handle.get(), &thread->context)) { 147 if (!GetThreadContext(thread_handle, &thread->context)) {
151 PLOG(ERROR) << "GetThreadContext failed"; 148 PLOG(ERROR) << "GetThreadContext failed";
152 return; 149 return;
153 } 150 }
154 151
155 if (!ResumeThread(thread_handle.get())) { 152 if (!ResumeThread(thread_handle)) {
156 PLOG(ERROR) << "ResumeThread failed"; 153 PLOG(ERROR) << "ResumeThread failed";
157 } 154 }
158 } 155 }
159 } 156 }
160 157
161 } // namespace 158 } // namespace
162 159
160 template <class Traits>
scottmg 2015/09/15 21:13:34 On XP, the "extended" version of this structure do
161 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
162 DCHECK(process_reader->threads_.empty());
163
164 scoped_ptr<uint8_t[]> buffer;
165 process_types::SYSTEM_PROCESS_INFORMATION<Traits>* process_information =
166 GetProcessInformation<Traits>(process_reader->process_, &buffer);
167 if (!process_information)
168 return;
169
170 for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) {
171 const process_types::SYSTEM_THREAD_INFORMATION<Traits>& thread_info =
172 process_information->Threads[i];
173 ProcessReaderWin::Thread thread;
174 thread.id = thread_info.ClientId.UniqueThread;
175
176 ScopedKernelHANDLE thread_handle(OpenThread(thread_info));
177 if (!thread_handle.is_valid())
178 continue;
179
180 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.
181 thread_handle.get(), &thread, process_reader->suspension_state_);
182
183 // TODO(scottmg): I believe we could reverse engineer the PriorityClass from
184 // the Priority, BasePriority, and
185 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 .
186 // MinidumpThreadWriter doesn't handle it yet in any case, so investigate
187 // both of those at the same time if it's useful.
188 thread.priority_class = NORMAL_PRIORITY_CLASS;
189
190 thread.priority = thread_info.Priority;
191
192 process_types::THREAD_BASIC_INFORMATION<Traits> thread_basic_info;
193 NTSTATUS status = crashpad::NtQueryInformationThread(
194 thread_handle.get(),
195 static_cast<THREADINFOCLASS>(ThreadBasicInformation),
196 &thread_basic_info,
197 sizeof(thread_basic_info),
198 nullptr);
199 if (!NT_SUCCESS(status)) {
200 NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread";
201 continue;
202 }
203
204 // Read the TIB (Thread Information Block) which is the first element of the
205 // TEB, for its stack fields.
206 process_types::NT_TIB<Traits> tib;
207 if (process_reader->ReadMemory(
208 thread_basic_info.TebBaseAddress, sizeof(tib), &tib)) {
209 // 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.
210 thread.stack_region_address = tib.StackLimit;
211 thread.stack_region_size = tib.StackBase - tib.StackLimit;
212 }
213 process_reader->threads_.push_back(thread);
214 }
215 }
216
163 ProcessReaderWin::Thread::Thread() 217 ProcessReaderWin::Thread::Thread()
164 : context(), 218 : context(),
165 id(0), 219 id(0),
166 teb(0), 220 teb(0),
167 stack_region_address(0), 221 stack_region_address(0),
168 stack_region_size(0), 222 stack_region_size(0),
169 suspend_count(0), 223 suspend_count(0),
170 priority_class(0), 224 priority_class(0),
171 priority(0) { 225 priority(0) {
172 } 226 }
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 } 290 }
237 291
238 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() { 292 const std::vector<ProcessReaderWin::Thread>& ProcessReaderWin::Threads() {
239 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 293 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
240 294
241 if (initialized_threads_) 295 if (initialized_threads_)
242 return threads_; 296 return threads_;
243 297
244 initialized_threads_ = true; 298 initialized_threads_ = true;
245 299
246 DCHECK(threads_.empty()); 300 if (process_info_.Is64Bit())
247 301 ReadThreadData<process_types::internal::Traits64>(this);
248 #if ARCH_CPU_32_BITS 302 else
249 using SizeTraits = process_types::internal::Traits32; 303 ReadThreadData<process_types::internal::Traits32>(this);
250 #else
251 using SizeTraits = process_types::internal::Traits64;
252 #endif
253 scoped_ptr<uint8_t[]> buffer;
254 process_types::SYSTEM_PROCESS_INFORMATION<SizeTraits>* process_information =
255 GetProcessInformation<SizeTraits>(process_, &buffer);
256 if (!process_information)
257 return threads_;
258
259 for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) {
260 const process_types::SYSTEM_EXTENDED_THREAD_INFORMATION<SizeTraits>&
261 thread_info = process_information->Threads[i];
262 Thread thread;
263 thread.id = thread_info.ClientId.UniqueThread;
264
265 FillThreadContextAndSuspendCount(thread_info, &thread, suspension_state_);
266
267 // TODO(scottmg): I believe we could reverse engineer the PriorityClass from
268 // the Priority, BasePriority, and
269 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100 .
270 // MinidumpThreadWriter doesn't handle it yet in any case, so investigate
271 // both of those at the same time if it's useful.
272 thread.priority_class = NORMAL_PRIORITY_CLASS;
273
274 thread.priority = thread_info.Priority;
275 thread.teb = thread_info.TebBase;
276
277 // While there are semi-documented fields in the thread structure called
278 // StackBase and StackLimit, they don't appear to be correct in practice (or
279 // at least, I don't know how to interpret them). Instead, read the TIB
280 // (Thread Information Block) which is the first element of the TEB, and use
281 // its stack fields.
282 process_types::NT_TIB<SizeTraits> tib;
283 if (ReadMemory(thread_info.TebBase, sizeof(tib), &tib)) {
284 // Note, "backwards" because of direction of stack growth.
285 thread.stack_region_address = tib.StackLimit;
286 thread.stack_region_size = tib.StackBase - tib.StackLimit;
287 }
288 threads_.push_back(thread);
289 }
290 304
291 return threads_; 305 return threads_;
292 } 306 }
293 307
294 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() { 308 const std::vector<ProcessInfo::Module>& ProcessReaderWin::Modules() {
295 INITIALIZATION_STATE_DCHECK_VALID(initialized_); 309 INITIALIZATION_STATE_DCHECK_VALID(initialized_);
296 310
297 if (!process_info_.Modules(&modules_)) { 311 if (!process_info_.Modules(&modules_)) {
298 LOG(ERROR) << "couldn't retrieve modules"; 312 LOG(ERROR) << "couldn't retrieve modules";
299 } 313 }
300 314
301 return modules_; 315 return modules_;
302 } 316 }
303 317
304 } // namespace crashpad 318 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698