]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2012-2013, 2015 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | ||
30 | /* | |
31 | * Corpses Overview | |
32 | * ================ | |
33 | * | |
34 | * A corpse is a state of process that is past the point of its death. This means that process has | |
35 | * completed all its termination operations like releasing file descriptors, mach ports, sockets and | |
36 | * other constructs used to identify a process. For all the processes this mimics the behavior as if | |
37 | * the process has died and no longer available by any means. | |
38 | * | |
39 | * Why do we need Corpses? | |
40 | * ----------------------- | |
41 | * For crash inspection we need to inspect the state and data that is associated with process so that | |
42 | * crash reporting infrastructure can build backtraces, find leaks etc. For example a crash | |
43 | * | |
44 | * Corpses functionality in kernel | |
45 | * =============================== | |
46 | * The corpse functionality is an extension of existing exception reporting mechanisms we have. The | |
47 | * exception_triage calls will try to deliver the first round of exceptions allowing | |
48 | * task/debugger/ReportCrash/launchd level exception handlers to respond to exception. If even after | |
49 | * notification the exception is not handled, then the process begins the death operations and during | |
50 | * proc_prepareexit, we decide to create a corpse for inspection. Following is a sample run through | |
51 | * of events and data shuffling that happens when corpses is enabled. | |
52 | * | |
53 | * * a process causes an exception during normal execution of threads. | |
54 | * * The exception generated by either mach(e.g GUARDED_MARCHPORT) or bsd(eg SIGABORT, GUARDED_FD | |
55 | * etc) side is passed through the exception_triage() function to follow the thread -> task -> host | |
56 | * level exception handling system. This set of steps are same as before and allow for existing | |
57 | * crash reporting systems (both internal and 3rd party) to catch and create reports as required. | |
58 | * * If above exception handling returns failed (when nobody handles the notification), then the | |
59 | * proc_prepareexit path has logic to decide to create corpse. | |
60 | * * The task_mark_corpse function allocates userspace vm memory and attaches the information | |
61 | * kcdata_descriptor_t to task->corpse_info field of task. | |
62 | * - All the task's threads are marked with the "inspection" flag which signals the termination | |
63 | * daemon to not reap them but hold until they are being inspected. | |
64 | * - task flags t_flags reflect the corpse bit and also a PENDING_CORPSE bit. PENDING_CORPSE | |
65 | * prevents task_terminate from stripping important data from task. | |
66 | * - It marks all the threads to terminate and return to AST for termination. | |
67 | * - The allocation logic takes into account the rate limiting policy of allowing only | |
68 | * TOTAL_CORPSES_ALLOWED in flight. | |
69 | * * The proc exit threads continues and collects required information in the allocated vm region. | |
70 | * Once complete it marks itself for termination. | |
71 | * * In the thread_terminate_self(), the last thread to enter will do a call to proc_exit(). | |
72 | * Following this is a check to see if task is marked for corpse notification and will | |
73 | * invoke the the task_deliver_crash_notification(). | |
74 | * * Once EXC_CORPSE_NOTIFY is delivered, it removes the PENDING_CORPSE flag from task (and | |
75 | * inspection flag from all its threads) and allows task_terminate to go ahead and continue | |
76 | * the mach task termination process. | |
77 | * * ASIDE: The rest of the threads that are reaching the thread_terminate_daemon() with the | |
78 | * inspection flag set are just bounced to another holding queue (crashed_threads_queue). | |
79 | * Only after the corpse notification these are pulled out from holding queue and enqueued | |
80 | * back to termination queue | |
81 | * | |
82 | * | |
83 | * Corpse info format | |
84 | * ================== | |
85 | * The kernel (task_mark_corpse()) makes a vm allocation in the dead task's vm space (with tag | |
86 | * VM_MEMORY_CORPSEINFO (80)). Within this memory all corpse information is saved by various | |
87 | * subsystems like | |
88 | * * bsd proc exit path may write down pid, parent pid, number of file descriptors etc | |
89 | * * mach side may append data regarding ledger usage, memory stats etc | |
90 | * See detailed info about the memory structure and format in kern_cdata.h documentation. | |
91 | * | |
92 | * Configuring Corpses functionality | |
93 | * ================================= | |
94 | * boot-arg: -no_corpses disables the corpse generation. This can be added/removed without affecting | |
95 | * any other subsystem. | |
96 | * TOTAL_CORPSES_ALLOWED : (recompilation required) - Changing this number allows for controlling | |
97 | * the number of corpse instances to be held for inspection before allowing memory to be reclaimed | |
98 | * by system. | |
99 | * CORPSEINFO_ALLOCATION_SIZE: is the default size of vm allocation. If in future there is much more | |
100 | * data to be put in, then please re-tune this parameter. | |
101 | * | |
102 | * Debugging/Visibility | |
103 | * ==================== | |
104 | * * lldbmacros for thread and task summary are updated to show "C" flag for corpse task/threads. | |
105 | * * there are macros to see list of threads in termination queue (dumpthread_terminate_queue) | |
106 | * and holding queue (dumpcrashed_thread_queue). | |
107 | * * In case of corpse creation is disabled of ignored then the system log is updated with | |
108 | * printf data with reason. | |
109 | * | |
110 | * Limitations of Corpses | |
111 | * ====================== | |
112 | * With holding off memory for inspection, it creates vm pressure which might not be desirable | |
113 | * on low memory devices. There are limits to max corpses being inspected at a time which is | |
114 | * marked by TOTAL_CORPSES_ALLOWED. | |
115 | * | |
116 | */ | |
117 | ||
118 | ||
119 | #include <kern/assert.h> | |
120 | #include <mach/mach_types.h> | |
121 | #include <mach/boolean.h> | |
122 | #include <mach/vm_param.h> | |
123 | #include <kern/kern_types.h> | |
124 | #include <kern/mach_param.h> | |
125 | #include <kern/thread.h> | |
126 | #include <kern/task.h> | |
127 | #include <corpses/task_corpse.h> | |
128 | #include <kern/kalloc.h> | |
129 | #include <kern/kern_cdata.h> | |
130 | #include <mach/mach_vm.h> | |
131 | ||
132 | unsigned long total_corpses_count = 0; | |
133 | unsigned long total_corpses_created = 0; | |
134 | boolean_t corpse_enabled_config = TRUE; | |
135 | ||
136 | kcdata_descriptor_t task_get_corpseinfo(task_t task); | |
137 | kcdata_descriptor_t task_crashinfo_alloc_init(mach_vm_address_t crash_data_p, unsigned size); | |
138 | kern_return_t task_crashinfo_destroy(kcdata_descriptor_t data); | |
139 | static kern_return_t task_crashinfo_get_ref(); | |
140 | static kern_return_t task_crashinfo_release_ref(); | |
141 | ||
142 | ||
143 | ||
144 | void corpses_init(){ | |
145 | char temp_buf[20]; | |
146 | if (PE_parse_boot_argn("-no_corpses", temp_buf, sizeof(temp_buf))) { | |
147 | corpse_enabled_config = FALSE; | |
148 | } | |
149 | } | |
150 | ||
151 | /* | |
152 | * Routine: corpses_enabled | |
153 | * returns FALSE if not enabled | |
154 | */ | |
155 | boolean_t corpses_enabled() | |
156 | { | |
157 | return corpse_enabled_config; | |
158 | } | |
159 | ||
160 | /* | |
161 | * Routine: task_crashinfo_get_ref() | |
162 | * Grab a slot at creating a corpse. | |
163 | * Returns: KERN_SUCCESS if the policy allows for creating a corpse. | |
164 | */ | |
165 | kern_return_t task_crashinfo_get_ref() | |
166 | { | |
167 | unsigned long counter = total_corpses_count; | |
168 | counter = OSIncrementAtomic((SInt32 *)&total_corpses_count); | |
169 | if (counter >= TOTAL_CORPSES_ALLOWED) { | |
170 | OSDecrementAtomic((SInt32 *)&total_corpses_count); | |
171 | return KERN_RESOURCE_SHORTAGE; | |
172 | } | |
173 | OSIncrementAtomicLong((volatile long *)&total_corpses_created); | |
174 | return KERN_SUCCESS; | |
175 | } | |
176 | ||
177 | /* | |
178 | * Routine: task_crashinfo_release_ref | |
179 | * release the slot for corpse being used. | |
180 | */ | |
181 | kern_return_t task_crashinfo_release_ref() | |
182 | { | |
183 | unsigned long __assert_only counter; | |
184 | counter = OSDecrementAtomic((SInt32 *)&total_corpses_count); | |
185 | assert(counter > 0); | |
186 | return KERN_SUCCESS; | |
187 | } | |
188 | ||
189 | ||
190 | kcdata_descriptor_t task_crashinfo_alloc_init(mach_vm_address_t crash_data_p, unsigned size) | |
191 | { | |
192 | if(KERN_SUCCESS != task_crashinfo_get_ref()) { | |
193 | return NULL; | |
194 | } | |
195 | ||
196 | return kcdata_memory_alloc_init(crash_data_p, TASK_CRASHINFO_BEGIN, size, KCFLAG_USE_COPYOUT); | |
197 | } | |
198 | ||
199 | ||
200 | /* | |
201 | * Free up the memory associated with task_crashinfo_data | |
202 | */ | |
203 | kern_return_t task_crashinfo_destroy(kcdata_descriptor_t data) | |
204 | { | |
205 | if (!data) { | |
206 | return KERN_INVALID_ARGUMENT; | |
207 | } | |
208 | ||
209 | task_crashinfo_release_ref(); | |
210 | return kcdata_memory_destroy(data); | |
211 | } | |
212 | ||
213 | /* | |
214 | * Routine: task_get_corpseinfo | |
215 | * params: task - task which has corpse info setup. | |
216 | * returns: crash info data attached to task. | |
217 | * NULL if task is null or has no corpse info | |
218 | */ | |
219 | kcdata_descriptor_t task_get_corpseinfo(task_t task) | |
220 | { | |
221 | kcdata_descriptor_t retval = NULL; | |
222 | if (task != NULL){ | |
223 | retval = task->corpse_info; | |
224 | } | |
225 | return retval; | |
226 | } | |
227 | ||
228 |