OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (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 | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "util/mac/process_reader.h" | |
16 | |
17 #include <AvailabilityMacros.h> | |
18 #include <mach/mach_vm.h> | |
19 #include <mach-o/loader.h> | |
20 | |
21 #include <algorithm> | |
22 | |
23 #include "base/logging.h" | |
24 #include "base/mac/mach_logging.h" | |
25 #include "base/mac/scoped_mach_port.h" | |
26 #include "base/mac/scoped_mach_vm.h" | |
27 | |
28 namespace { | |
29 | |
30 void MachTimeValueToTimeval(const time_value& mach, timeval* tv) { | |
31 tv->tv_sec = mach.seconds; | |
32 tv->tv_usec = mach.microseconds; | |
33 } | |
34 | |
35 kern_return_t MachVMRegionRecurseDeepest(mach_port_t task, | |
36 mach_vm_address_t* address, | |
37 mach_vm_size_t* size, | |
38 natural_t* depth, | |
39 vm_prot_t* protection, | |
40 unsigned int* user_tag) { | |
41 vm_region_submap_short_info_64 submap_info; | |
42 mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; | |
43 while (true) { | |
44 kern_return_t kr = mach_vm_region_recurse( | |
45 task, | |
46 address, | |
47 size, | |
48 depth, | |
49 reinterpret_cast<vm_region_recurse_info_t>(&submap_info), | |
50 &count); | |
51 if (kr != KERN_SUCCESS) { | |
52 return kr; | |
53 } | |
54 | |
55 if (!submap_info.is_submap) { | |
56 *protection = submap_info.protection; | |
57 *user_tag = submap_info.user_tag; | |
58 return KERN_SUCCESS; | |
59 } | |
60 | |
61 ++*depth; | |
62 } | |
63 } | |
64 | |
65 } // namespace | |
66 | |
67 namespace crashpad { | |
68 | |
69 ProcessReaderThread::ProcessReaderThread() | |
70 : thread_context(), | |
71 float_context(), | |
72 debug_context(), | |
73 id(0), | |
74 stack_region_address(0), | |
75 stack_region_size(0), | |
76 thread_specific_data_address(0), | |
77 port(MACH_PORT_NULL), | |
78 suspend_count(0), | |
79 priority(0) { | |
80 } | |
81 | |
82 ProcessReaderModule::ProcessReaderModule() : name(), address(0), timestamp(0) { | |
83 } | |
84 | |
85 ProcessReaderModule::~ProcessReaderModule() { | |
86 } | |
87 | |
88 ProcessReader::ProcessReader() | |
89 : kern_proc_info_(), | |
90 threads_(), | |
91 modules_(), | |
92 task_memory_(), | |
93 task_(MACH_PORT_NULL), | |
94 initialized_(), | |
95 is_64_bit_(false), | |
96 initialized_threads_(false), | |
97 initialized_modules_(false) { | |
98 } | |
99 | |
100 ProcessReader::~ProcessReader() { | |
101 for (ProcessReaderThread& thread : threads_) { | |
Robert Sesek
2014/08/21 15:26:54
const& ?
| |
102 kern_return_t kr = mach_port_deallocate(mach_task_self(), thread.port); | |
103 MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; | |
104 } | |
105 } | |
106 | |
107 bool ProcessReader::Initialize(mach_port_t task) { | |
108 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | |
109 | |
110 pid_t pid; | |
111 kern_return_t kr = pid_for_task(task, &pid); | |
112 if (kr != KERN_SUCCESS) { | |
113 MACH_LOG(ERROR, kr) << "pid_for_task"; | |
114 return false; | |
115 } | |
116 | |
117 int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; | |
118 size_t len = sizeof(kern_proc_info_); | |
119 if (sysctl(mib, arraysize(mib), &kern_proc_info_, &len, NULL, 0) != 0) { | |
120 PLOG(ERROR) << "sysctl for pid " << pid; | |
121 return false; | |
122 } | |
123 | |
124 DCHECK_EQ(kern_proc_info_.kp_proc.p_pid, pid); | |
125 | |
126 is_64_bit_ = kern_proc_info_.kp_proc.p_flag & P_LP64; | |
127 | |
128 task_memory_.reset(new TaskMemory(task)); | |
129 task_ = task; | |
130 | |
131 INITIALIZATION_STATE_SET_VALID(initialized_); | |
132 return true; | |
133 } | |
134 | |
135 void ProcessReader::StartTime(timeval* start_time) const { | |
136 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
137 *start_time = kern_proc_info_.kp_proc.p_starttime; | |
138 } | |
139 | |
140 bool ProcessReader::CPUTimes(timeval* user_time, timeval* system_time) const { | |
141 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
142 | |
143 // Calculate user and system time the same way the kernel does for | |
144 // getrusage(). See 10.9.2 xnu-2422.90.20/bsd/kern/kern_resource.c calcru(). | |
145 timerclear(user_time); | |
146 timerclear(system_time); | |
147 | |
148 // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO. | |
149 // TASK_BASIC_INFO_64_COUNT is equivalent and works on earlier systems. | |
150 task_basic_info_64 task_basic_info; | |
151 mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT; | |
152 kern_return_t kr = task_info(task_, | |
153 TASK_BASIC_INFO_64, | |
154 reinterpret_cast<task_info_t>(&task_basic_info), | |
155 &task_basic_info_count); | |
156 if (kr != KERN_SUCCESS) { | |
157 MACH_LOG(WARNING, kr) << "task_info TASK_BASIC_INFO_64"; | |
158 return false; | |
159 } | |
160 | |
161 task_thread_times_info_data_t task_thread_times; | |
162 mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT; | |
163 kr = task_info(task_, | |
164 TASK_THREAD_TIMES_INFO, | |
165 reinterpret_cast<task_info_t>(&task_thread_times), | |
166 &task_thread_times_count); | |
167 if (kr != KERN_SUCCESS) { | |
168 MACH_LOG(WARNING, kr) << "task_info TASK_THREAD_TIMES"; | |
169 return false; | |
170 } | |
171 | |
172 MachTimeValueToTimeval(task_basic_info.user_time, user_time); | |
173 MachTimeValueToTimeval(task_basic_info.system_time, system_time); | |
174 | |
175 timeval thread_user_time; | |
176 MachTimeValueToTimeval(task_thread_times.user_time, &thread_user_time); | |
177 timeval thread_system_time; | |
178 MachTimeValueToTimeval(task_thread_times.system_time, &thread_system_time); | |
179 | |
180 timeradd(user_time, &thread_user_time, user_time); | |
181 timeradd(system_time, &thread_system_time, system_time); | |
182 | |
183 return true; | |
184 } | |
185 | |
186 const std::vector<ProcessReaderThread>& ProcessReader::Threads() { | |
187 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
188 | |
189 if (!initialized_threads_) { | |
190 InitializeThreads(); | |
191 } | |
192 | |
193 return threads_; | |
194 } | |
195 | |
196 const std::vector<ProcessReaderModule>& ProcessReader::Modules() { | |
197 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
198 | |
199 if (!initialized_modules_) { | |
200 InitializeModules(); | |
201 } | |
202 | |
203 return modules_; | |
204 } | |
205 | |
206 void ProcessReader::InitializeThreads() { | |
207 DCHECK(!initialized_threads_); | |
208 DCHECK(threads_.empty()); | |
209 | |
210 initialized_threads_ = true; | |
211 | |
212 thread_act_array_t threads; | |
213 mach_msg_type_number_t thread_count = 0; | |
214 kern_return_t kr = task_threads(task_, &threads, &thread_count); | |
215 if (kr != KERN_SUCCESS) { | |
216 MACH_LOG(WARNING, kr) << "task_threads"; | |
217 return; | |
218 } | |
219 | |
220 base::mac::ScopedMachVM threads_vm( | |
221 reinterpret_cast<vm_address_t>(threads), | |
222 mach_vm_round_page(thread_count * sizeof(*threads))); | |
223 | |
224 for (size_t index = 0; index < thread_count; ++index) { | |
225 ProcessReaderThread thread; | |
226 thread.port = threads[index]; | |
227 base::mac::ScopedMachSendRight thread_port_owner(thread.port); | |
Robert Sesek
2014/08/21 15:26:54
Maybe leave a comment as to why you scope it here,
Mark Mentovai
2014/08/21 21:40:52
rsesek wrote:
| |
228 | |
229 #if defined(ARCH_CPU_X86_FAMILY) | |
Robert Sesek
2014/08/21 15:26:54
When Apple produces a ARM-based Mac, this could ge
| |
230 const thread_state_flavor_t kThreadStateFlavor = | |
231 Is64Bit() ? x86_THREAD_STATE64 : x86_THREAD_STATE32; | |
232 mach_msg_type_number_t thread_state_count = | |
233 Is64Bit() ? x86_THREAD_STATE64_COUNT : x86_THREAD_STATE32_COUNT; | |
234 | |
235 // TODO(mark): Use the AVX variants instead of the FLOAT variants? They’re | |
236 // supported on 10.6 and later. | |
237 const thread_state_flavor_t kFloatStateFlavor = | |
238 Is64Bit() ? x86_FLOAT_STATE64 : x86_FLOAT_STATE32; | |
239 mach_msg_type_number_t float_state_count = | |
240 Is64Bit() ? x86_FLOAT_STATE64_COUNT : x86_FLOAT_STATE32_COUNT; | |
241 | |
242 const thread_state_flavor_t kDebugStateFlavor = | |
243 Is64Bit() ? x86_DEBUG_STATE64 : x86_DEBUG_STATE32; | |
244 mach_msg_type_number_t debug_state_count = | |
245 Is64Bit() ? x86_DEBUG_STATE64_COUNT : x86_DEBUG_STATE32_COUNT; | |
246 #endif | |
247 | |
248 kr = thread_get_state( | |
249 thread.port, | |
250 kThreadStateFlavor, | |
251 reinterpret_cast<thread_state_t>(&thread.thread_context), | |
252 &thread_state_count); | |
253 if (kr != KERN_SUCCESS) { | |
254 MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")"; | |
255 continue; | |
256 } | |
257 | |
258 kr = thread_get_state( | |
259 thread.port, | |
260 kFloatStateFlavor, | |
261 reinterpret_cast<thread_state_t>(&thread.float_context), | |
262 &float_state_count); | |
263 if (kr != KERN_SUCCESS) { | |
264 MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")"; | |
265 continue; | |
266 } | |
267 | |
268 kr = thread_get_state( | |
269 thread.port, | |
270 kDebugStateFlavor, | |
271 reinterpret_cast<thread_state_t>(&thread.debug_context), | |
272 &debug_state_count); | |
273 if (kr != KERN_SUCCESS) { | |
274 MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")"; | |
275 continue; | |
276 } | |
277 | |
278 thread_basic_info basic_info; | |
279 mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; | |
280 kr = thread_info(thread.port, | |
281 THREAD_BASIC_INFO, | |
282 reinterpret_cast<thread_info_t>(&basic_info), | |
283 &count); | |
284 if (kr != KERN_SUCCESS) { | |
285 MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)"; | |
286 } else { | |
287 thread.suspend_count = basic_info.suspend_count; | |
288 } | |
289 | |
290 thread_identifier_info identifier_info; | |
291 count = THREAD_IDENTIFIER_INFO_COUNT; | |
292 kr = thread_info(thread.port, | |
293 THREAD_IDENTIFIER_INFO, | |
294 reinterpret_cast<thread_info_t>(&identifier_info), | |
295 &count); | |
296 if (kr != KERN_SUCCESS) { | |
297 MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)"; | |
298 } else { | |
299 thread.id = identifier_info.thread_id; | |
300 | |
301 // thread_identifier_info::thread_handle contains the base of the | |
302 // thread-specific data area, which on x86 and x86_64 is the thread’s base | |
303 // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c | |
304 // thread_info_internal() gets the value from | |
305 // machine_thread::cthread_self, which is the same value used to set the | |
306 // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c | |
307 // act_machine_switch_pcb(). | |
308 // | |
309 // This address is the internal pthread’s _pthread::tsd[], an array of | |
310 // void* values that can be indexed by pthread_key_t values. | |
311 thread.thread_specific_data_address = identifier_info.thread_handle; | |
312 } | |
313 | |
314 thread_precedence_policy precedence; | |
315 count = THREAD_PRECEDENCE_POLICY_COUNT; | |
316 boolean_t get_default = FALSE; | |
317 kr = thread_policy_get(thread.port, | |
318 THREAD_PRECEDENCE_POLICY, | |
319 reinterpret_cast<thread_policy_t>(&precedence), | |
320 &count, | |
321 &get_default); | |
322 if (kr != KERN_SUCCESS) { | |
323 MACH_LOG(INFO, kr) << "thread_policy_get"; | |
324 } else { | |
325 thread.priority = precedence.importance; | |
326 } | |
327 | |
328 #if defined(ARCH_CPU_X86_FAMILY) | |
329 mach_vm_address_t stack_pointer = Is64Bit() | |
330 ? thread.thread_context.t64.__rsp | |
331 : thread.thread_context.t32.__esp; | |
332 #endif | |
333 | |
334 thread.stack_region_address = | |
335 CalculateStackRegion(stack_pointer, &thread.stack_region_size); | |
336 | |
337 // In threads_, the send right will be owned by this object. | |
338 ignore_result(thread_port_owner.release()); | |
339 | |
340 threads_.push_back(thread); | |
341 } | |
342 } | |
343 | |
344 void ProcessReader::InitializeModules() { | |
345 DCHECK(!initialized_modules_); | |
346 DCHECK(modules_.empty()); | |
347 | |
348 initialized_modules_ = true; | |
349 | |
350 // TODO(mark): Complete this implementation. The implementation depends on | |
351 // process_types, which cannot land yet because it depends on this file, | |
352 // process_reader. This temporary “cut” was made to avoid a review that’s too | |
353 // large. Yes, this circular dependency is unfortunate. Suggestions are | |
354 // welcome. | |
355 } | |
356 | |
357 mach_vm_address_t ProcessReader::CalculateStackRegion( | |
358 mach_vm_address_t stack_pointer, | |
359 mach_vm_size_t* stack_region_size) { | |
360 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
361 | |
362 // For pthreads, it may be possible to compute the stack region based on the | |
363 // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct | |
364 // for a thread can be located at TSD slot 0, or the known offsets of | |
365 // stackaddr and stacksize from the TSD area could be used. | |
366 mach_vm_address_t region_base = stack_pointer; | |
367 mach_vm_size_t region_size; | |
368 natural_t depth = 0; | |
369 vm_prot_t protection; | |
370 unsigned int user_tag; | |
371 kern_return_t kr = MachVMRegionRecurseDeepest( | |
372 task_, ®ion_base, ®ion_size, &depth, &protection, &user_tag); | |
373 if (kr != KERN_SUCCESS) { | |
374 MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; | |
375 *stack_region_size = 0; | |
376 return 0; | |
377 } | |
378 | |
379 if (region_base > stack_pointer) { | |
380 // There’s nothing mapped at the stack pointer’s address. Something may have | |
381 // trashed the stack pointer. Note that this shouldn’t happen for a normal | |
382 // stack guard region violation because the guard region is mapped but has | |
383 // VM_PROT_NONE protection. | |
384 *stack_region_size = 0; | |
385 return 0; | |
386 } | |
387 | |
388 mach_vm_address_t start_address = stack_pointer; | |
389 | |
390 if ((protection & VM_PROT_READ) == 0) { | |
391 // If the region isn’t readable, the stack pointer probably points to the | |
392 // guard region. Don’t include it as part of the stack, and don’t include | |
393 // anything at any lower memory address. The code below may still possibly | |
394 // find the real stack region at a memory address higher than this region. | |
395 start_address = region_base + region_size; | |
396 } else { | |
397 #if defined(ARCH_CPU_X86_FAMILY) | |
398 if (Is64Bit()) { | |
Robert Sesek
2014/08/21 15:26:54
You could pull this into a LocateRedZone(mach_vm_a
| |
399 // x86_64 has a red zone. See AMD64 ABI 0.99.6, | |
400 // http://www.x86-64.org/documentation/abi.pdf, section 3.2.2, “The Stack | |
401 // Frame”. | |
402 const mach_vm_size_t kRedZoneSize = 128; | |
Robert Sesek
2014/08/21 15:26:54
Optional: Since this size is mandated by the ABI,
Mark Mentovai
2014/08/21 21:40:51
rsesek wrote:
| |
403 mach_vm_address_t red_zone_base = | |
404 start_address >= kRedZoneSize ? start_address - kRedZoneSize : 0; | |
405 bool red_zone_ok = false; | |
406 if (red_zone_base >= region_base) { | |
407 // The red zone is within the region already discovered. | |
408 red_zone_ok = true; | |
409 } else if (red_zone_base < region_base && user_tag == VM_MEMORY_STACK) { | |
410 // Probe to see if there’s a region immediately below the one already | |
411 // discovered. | |
412 mach_vm_address_t red_zone_region_base = red_zone_base; | |
413 mach_vm_size_t red_zone_region_size; | |
414 natural_t red_zone_depth = 0; | |
415 vm_prot_t red_zone_protection; | |
416 unsigned int red_zone_user_tag; | |
417 kr = MachVMRegionRecurseDeepest(task_, | |
418 &red_zone_region_base, | |
419 &red_zone_region_size, | |
420 &red_zone_depth, | |
421 &red_zone_protection, | |
422 &red_zone_user_tag); | |
423 if (kr != KERN_SUCCESS) { | |
424 MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; | |
425 start_address = region_base; | |
426 } else if (red_zone_region_base + red_zone_region_size == region_base && | |
427 (red_zone_protection & VM_PROT_READ) != 0 && | |
428 red_zone_user_tag == VM_MEMORY_STACK) { | |
429 // The region containing the red zone is immediately below the region | |
430 // already found, it’s readable (not the guard region), and they’ve | |
Robert Sesek
2014/08/21 15:26:54
nit: Pronoun agreement in this sentence.
| |
431 // got the same user tag, so merge them. | |
432 red_zone_ok = true; | |
433 region_base -= red_zone_region_size; | |
434 region_size += red_zone_region_size; | |
435 } | |
436 } | |
437 | |
438 if (red_zone_ok) { | |
439 // Begin capturing from the base of the red zone (but not the entire | |
440 // region that encompasses the red zone). | |
441 start_address = red_zone_base; | |
442 } else { | |
443 // The red zone would go lower into another region in memory, but no | |
444 // region was found. Memory can only be captured to an address as low as | |
445 // the base address of the region already found. | |
446 start_address = region_base; | |
447 } | |
448 } | |
449 #endif | |
450 | |
451 // Regardless of whether the ABI requires a red zone, capture up to | |
452 // kExtraCaptureSize additional bytes of stack, but only if present in the | |
453 // region (or regions) already found. | |
Robert Sesek
2014/08/21 15:26:54
nit: Missing a verb.
| |
454 const mach_vm_size_t kExtraCaptureSize = 128; | |
455 start_address = std::max(start_address >= kExtraCaptureSize | |
456 ? start_address - kExtraCaptureSize | |
457 : start_address, | |
458 region_base); | |
459 | |
460 // Align start_address to a 16-byte boundary, which can help readers by | |
461 // ensuring that data is aligned properly. This could page-align instead, | |
462 // but that might be wasteful. | |
463 const mach_vm_size_t kDesiredAlignment = 16; | |
464 start_address &= ~(kDesiredAlignment - 1); | |
465 DCHECK_GE(start_address, region_base); | |
466 } | |
467 | |
468 region_size -= (start_address - region_base); | |
469 region_base = start_address; | |
470 | |
471 mach_vm_size_t total_region_size = region_size; | |
472 | |
473 // The stack region may have gotten split up into multiple abutting regions. | |
474 // Try to coalesce them. This frequently happens for the main thread’s stack | |
475 // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region | |
476 // is split up due to an mprotect() or vm_protect() call. | |
477 // | |
478 // Stack regions created by the kernel and the pthreads library will be marked | |
479 // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions | |
480 // with the same tag should find an entire stack region. Checking that the | |
481 // protection on individual regions is not VM_PROT_NONE should guarantee that | |
482 // this algorithm doesn’t collect map entries belonging to another thread’s | |
483 // stack: well-behaved stacks (such as those created by the kernel and the | |
484 // pthreads library) have VM_PROT_NONE guard regions at their low-address | |
485 // ends. | |
486 // | |
487 // Other stack regions may not be so well-behaved and thus if user_tag is not | |
488 // VM_MEMORY_STACK, the single region that was found is used as-is without | |
489 // trying to merge it with other adjacent regions. | |
490 if (user_tag == VM_MEMORY_STACK) { | |
491 mach_vm_address_t try_address = region_base; | |
492 mach_vm_address_t original_try_address; | |
493 | |
494 while (try_address += region_size, | |
495 original_try_address = try_address, | |
496 (kr = MachVMRegionRecurseDeepest(task_, | |
497 &try_address, | |
498 ®ion_size, | |
499 &depth, | |
500 &protection, | |
501 &user_tag) == KERN_SUCCESS) && | |
502 try_address == original_try_address && | |
503 (protection & VM_PROT_READ) != 0 && | |
504 user_tag == VM_MEMORY_STACK) { | |
505 total_region_size += region_size; | |
506 } | |
507 | |
508 if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) { | |
509 // Tolerate KERN_INVALID_ADDRESS because it will be returned when there | |
510 // are no more regions in the map at or above the specified |try_address|. | |
511 MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; | |
512 } | |
513 } | |
514 | |
515 *stack_region_size = total_region_size; | |
516 return region_base; | |
517 } | |
518 | |
519 } // namespace crashpad | |
OLD | NEW |