]>
Commit | Line | Data |
---|---|---|
39236c6e A |
1 | """ Please make sure you read the README file COMPLETELY BEFORE reading anything below. |
2 | It is very critical that you read coding guidelines in Section E in README file. | |
3 | """ | |
4 | from xnu import * | |
5 | import sys, shlex | |
6 | from utils import * | |
7 | from process import * | |
fe8ab488 A |
8 | from atm import * |
9 | from bank import * | |
3e170ce0 | 10 | from waitq import * |
39037602 | 11 | from ioreg import * |
39236c6e A |
12 | import xnudefines |
13 | ||
d9a64523 A |
14 | @header("{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <20s}".format("task", "pid", '#acts', "tablesize", "command")) |
15 | def GetTaskIPCSummary(task, show_busy = False): | |
39236c6e A |
16 | """ Display a task's ipc summary. |
17 | params: | |
18 | task : core.value represeting a Task in kernel | |
19 | returns | |
20 | str - string of ipc info for the task | |
21 | """ | |
22 | out_string = '' | |
d9a64523 A |
23 | format_string = "{0: <#020x} {1: <6d} {2: <6d} {3: <10d} {4: <20s}" |
24 | busy_format = " {0: <10d} {1: <6d}" | |
25 | proc_name = '' | |
26 | if not task.active: | |
27 | proc_name = 'terminated: ' | |
28 | if task.halting: | |
29 | proc_name += 'halting: ' | |
39236c6e | 30 | pval = Cast(task.bsd_info, 'proc *') |
d9a64523 A |
31 | if int(pval) != 0: |
32 | proc_name += str(pval.p_comm) | |
33 | elif int(task.task_imp_base) != 0 and hasattr(task.task_imp_base, 'iit_procname'): | |
34 | proc_name += str(task.task_imp_base.iit_procname) | |
39236c6e | 35 | table_size = int(task.itk_space.is_table_size) |
39236c6e | 36 | out_string += format_string.format(task, pval.p_pid, task.thread_count, table_size, proc_name) |
d9a64523 A |
37 | if show_busy: |
38 | nbusy, nmsgs = GetTaskBusyPortsSummary(task) | |
39 | out_string += busy_format.format(nbusy, nmsgs) | |
40 | return (out_string, table_size, nbusy, nmsgs) | |
41 | return (out_string, table_size) | |
42 | ||
43 | @header("{0: <20s} {1: <6s} {2: <6s} {3: <10s} {4: <20s} {5: <10s} {6: <6s}".format("task", "pid", '#acts', "tablesize", "command", "#busyports", "#kmsgs")) | |
44 | def GetTaskBusyIPCSummary(task): | |
45 | return GetTaskIPCSummary(task, True) | |
46 | ||
47 | def GetTaskBusyPortsSummary(task): | |
48 | isp = task.itk_space | |
49 | i = 0 | |
50 | nbusy = 0 | |
51 | nmsgs = 0 | |
52 | while i < isp.is_table_size: | |
53 | iep = addressof(isp.is_table[i]) | |
54 | if iep.ie_bits & 0x00020000: | |
55 | port = Cast(iep.ie_object, 'ipc_port_t') | |
56 | if port.ip_messages.data.port.msgcount > 0: | |
57 | nbusy += 1 | |
58 | nmsgs += port.ip_messages.data.port.msgcount | |
59 | i = i + 1 | |
60 | return (nbusy, nmsgs) | |
61 | ||
39236c6e A |
62 | |
63 | @header("{0: <20s} {1: <28s} {2: <12s} {3: <6s} {4: <4s} {5: <20s} {6: <4s}\n".format( | |
64 | "port", "mqueue", "recvname", "flags", "refs", "recvname", "dest")) | |
3e170ce0 | 65 | def PrintPortSummary(port, show_kmsg_summary=True, prefix=""): |
39236c6e A |
66 | """ Display a port's summary |
67 | params: | |
68 | port : core.value representing a port in the kernel | |
69 | returns | |
70 | str : string of ipc info for the given port | |
71 | """ | |
72 | out_string = "" | |
73 | portp = Cast(port, 'struct ipc_port *') | |
74 | destspacep = kern.GetValueFromAddress(0, 'struct ipc_space *') | |
75 | spacep = portp.data.receiver | |
76 | format_string = "{0: #019x} {1: #019x} {2: <8s} {3: #011x} {4: <5s} {5: #05x} {6: #019x} {7: <16s}\n" | |
77 | if portp.ip_object.io_bits & 0x80000000: | |
78 | out_string += prefix + format_string.format( | |
79 | unsigned(portp), addressof(portp.ip_messages), ' '*8, | |
80 | unsigned(portp.ip_messages.data.port.receiver_name), | |
81 | "APort", portp.ip_object.io_references, | |
82 | unsigned(portp.ip_messages.data.port.receiver_name), | |
83 | GetPortDestProc(portp)) | |
84 | else: | |
85 | out_string += prefix + format_string.format( | |
86 | unsigned(portp), addressof(portp.ip_messages), ' '*8, | |
87 | unsigned(portp.ip_messages.data.port.receiver_name), | |
88 | "DPort", portp.ip_object.io_references, unsigned(portp), | |
89 | "inactive-port") | |
3e170ce0 | 90 | print out_string |
39236c6e A |
91 | if show_kmsg_summary: |
92 | kmsgp = Cast(portp.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t') | |
3e170ce0 A |
93 | if unsigned(kmsgp): |
94 | print prefix + GetKMsgSummary.header + prefix + GetKMsgSummary(kmsgp, prefix) | |
95 | kmsgheadp = kmsgp | |
39236c6e | 96 | kmsgp = kmsgp.ikm_next |
3e170ce0 A |
97 | while (kmsgp) != (kmsgheadp): |
98 | print prefix + GetKMsgSummary(kmsgp, prefix) | |
99 | kmsgp = kmsgp.ikm_next | |
100 | return | |
39236c6e A |
101 | |
102 | def GetPortDestProc(portp): | |
103 | """ Display the name and pid of a given port's receiver | |
104 | params: | |
105 | portp : core.value representing a pointer to a port in the kernel | |
106 | destspacep : core.value representing a pointer to an ipc_space | |
107 | returns: | |
108 | str : string containing receiver's name and pid | |
109 | """ | |
110 | spacep = portp.data.receiver | |
111 | out_str = "Not found" | |
112 | for tsk in kern.tasks: | |
113 | if tsk.itk_space == spacep: | |
114 | if tsk.bsd_info: | |
115 | destprocp = Cast(tsk.bsd_info, 'struct proc *') | |
116 | out_str = "{0:s}({1: <d})".format(destprocp.p_comm, destprocp.p_pid) | |
117 | else: | |
fe8ab488 | 118 | out_str = "unknown" |
39236c6e A |
119 | break |
120 | ||
121 | return out_str | |
122 | ||
d9a64523 A |
123 | |
124 | def GetPortDispositionString(disp): | |
125 | if (disp < 0): ## use negative numbers for request ports | |
126 | portname = 'notify' | |
127 | if disp == -1: | |
128 | disp_str = 'reqNS' | |
129 | elif disp == -2: | |
130 | disp_str = 'reqPD' | |
131 | elif disp == -3: | |
132 | disp_str = 'reqSPa' | |
133 | elif disp == -4: | |
134 | disp_str = 'reqSPr' | |
135 | elif disp == -5: | |
136 | disp_str = 'reqSPra' | |
137 | else: | |
138 | disp_str = '-X' | |
139 | ## These dispositions should match those found in osfmk/mach/message.h | |
140 | elif disp == 16: | |
141 | disp_str = 'R' ## receive | |
142 | elif disp == 24: | |
143 | disp_str = 'dR' ## dispose receive | |
144 | elif disp == 17: | |
145 | disp_str = 'S' ## (move) send | |
146 | elif disp == 19: | |
147 | disp_str = 'cS' ## copy send | |
148 | elif disp == 20: | |
149 | disp_str = 'mS' ## make send | |
150 | elif disp == 25: | |
151 | disp_str = 'dS' ## dispose send | |
152 | elif disp == 18: | |
153 | disp_str = 'O' ## send-once | |
154 | elif disp == 21: | |
155 | disp_str = 'mO' ## make send-once | |
156 | elif disp == 26: | |
157 | disp_str = 'dO' ## dispose send-once | |
158 | ## faux dispositions used to string-ify IPC entry types | |
159 | elif disp == 100: | |
160 | disp_str = 'PS' ## port set | |
161 | elif disp == 101: | |
162 | disp_str = 'dead' ## dead name | |
163 | elif disp == 102: | |
164 | disp_str = 'L' ## LABELH | |
165 | elif disp == 103: | |
166 | disp_str = 'V' ## Thread voucher (thread->ith_voucher->iv_port) | |
167 | ## Catch-all | |
168 | else: | |
169 | disp_str = 'X' ## invalid | |
170 | return disp_str | |
171 | ||
172 | ||
3e170ce0 A |
173 | @header("{:<20s} {:<28s} {:<12s} {:<8s} {:<6s} {:<19s} {:<26s} {:<26s}\n".format( |
174 | "", "kmsg", "msgid", "disp", "size", "reply-port", "source", "destination")) | |
175 | def GetKMsgSummary(kmsgp, prefix_str=""): | |
39236c6e A |
176 | """ Display a summary for type ipc_kmsg_t |
177 | params: | |
178 | kmsgp : core.value representing the given ipc_kmsg_t struct | |
179 | returns: | |
180 | str : string of summary info for the given ipc_kmsg_t instance | |
181 | """ | |
182 | kmsghp = kmsgp.ikm_header | |
183 | kmsgh = dereference(kmsghp) | |
184 | out_string = "" | |
3e170ce0 A |
185 | out_string += "{0: <20s} {1: <#019x} {2: <8s} {3: <#011x} ".format( |
186 | ' ', unsigned(kmsgp), ' '*8, kmsgh.msgh_id) | |
187 | prefix_str = "{0: <20s} ".format(' ') + prefix_str | |
188 | disposition = "" | |
189 | bits = kmsgh.msgh_bits & 0xff | |
39236c6e | 190 | |
3e170ce0 A |
191 | # remote port |
192 | if bits == 17: | |
193 | disposition = "rS" | |
194 | elif bits == 18: | |
195 | disposition = "rO" | |
196 | else : | |
197 | disposition = "rX" # invalid | |
198 | ||
199 | out_string += "{0: <2s}".format(disposition) | |
200 | ||
201 | # local port | |
202 | disposition = "" | |
203 | bits = (kmsgh.msgh_bits & 0xff00) >> 8 | |
204 | ||
205 | if bits == 17: | |
206 | disposition = "lS" | |
207 | elif bits == 18: | |
208 | disposition = "lO" | |
209 | elif bits == 0: | |
210 | disposition = "l-" | |
39236c6e | 211 | else: |
3e170ce0 A |
212 | disposition = "lX" # invalid |
213 | ||
214 | out_string += "{0: <2s}".format(disposition) | |
39236c6e | 215 | |
3e170ce0 A |
216 | # voucher |
217 | disposition = "" | |
218 | bits = (kmsgh.msgh_bits & 0xff0000) >> 16 | |
219 | ||
220 | if bits == 17: | |
221 | disposition = "vS" | |
222 | elif bits == 0: | |
223 | disposition = "v-" | |
39236c6e | 224 | else: |
3e170ce0 A |
225 | disposition = "vX" |
226 | ||
227 | out_string += "{0: <2s}".format(disposition) | |
228 | ||
229 | # complex message | |
230 | if kmsgh.msgh_bits & 0x80000000: | |
231 | out_string += "{0: <1s}".format("c") | |
39236c6e | 232 | else: |
3e170ce0 A |
233 | out_string += "{0: <1s}".format("s") |
234 | ||
235 | # importance boost | |
236 | if kmsgh.msgh_bits & 0x20000000: | |
237 | out_string += "{0: <1s}".format("I") | |
238 | else: | |
239 | out_string += "{0: <1s}".format("-") | |
39236c6e | 240 | |
fe8ab488 A |
241 | dest_proc_name = "" |
242 | if kmsgp.ikm_header.msgh_remote_port: | |
243 | dest_proc_name = GetDestinationProcessFromPort(kmsgp.ikm_header.msgh_remote_port) | |
244 | ||
245 | out_string += "{0: ^6d} {1: <#019x} {2: <26s} {3: <26s}\n".format( | |
246 | unsigned(kmsgh.msgh_size), unsigned(kmsgh.msgh_local_port), | |
247 | GetKMsgSrc(kmsgp), dest_proc_name) | |
3e170ce0 A |
248 | |
249 | if kmsgh.msgh_bits & 0x80000000: | |
d9a64523 A |
250 | out_string += prefix_str + "\t" + GetKMsgComplexBodyDesc.header + "\n" |
251 | out_string += prefix_str + "\t" + GetKMsgComplexBodyDesc(kmsgp, prefix_str + "\t") + "\n" | |
3e170ce0 A |
252 | |
253 | return out_string | |
254 | ||
255 | @header("{: <20s} {: <20s} {: <10s}".format("descriptor", "address", "size")) | |
256 | def GetMachMsgOOLDescriptorSummary(desc): | |
257 | """ Returns description for mach_msg_ool_descriptor_t * object | |
258 | """ | |
259 | format_string = "{: <#020x} {: <#020x} {: <#010x}" | |
260 | out_string = format_string.format(desc, desc.address, desc.size) | |
39236c6e A |
261 | return out_string |
262 | ||
d9a64523 A |
263 | |
264 | def GetKmsgDescriptors(kmsgp): | |
265 | """ Get a list of descriptors in a complex message | |
266 | """ | |
267 | kmsghp = kmsgp.ikm_header | |
268 | kmsgh = dereference(kmsghp) | |
269 | if not (kmsgh.msgh_bits & 0x80000000): | |
270 | return [] | |
271 | ## Something in the python/lldb types is not getting alignment correct here. | |
272 | ## I'm grabbing a pointer to the body manually, and using tribal knowledge | |
273 | ## of the location of the descriptor count to get this correct | |
274 | body = Cast(addressof(Cast(addressof(kmsgh), 'char *')[sizeof(kmsgh)]), 'mach_msg_body_t *') | |
275 | #dsc_count = body.msgh_descriptor_count | |
276 | dsc_count = dereference(Cast(body, 'uint32_t *')) | |
277 | #dschead = Cast(addressof(body[1]), 'mach_msg_descriptor_t *') | |
278 | dschead = Cast(addressof(Cast(addressof(body[0]), 'char *')[sizeof('uint32_t')]), 'mach_msg_descriptor_t *') | |
279 | dsc_list = [] | |
280 | for i in range(dsc_count): | |
281 | dsc_list.append(dschead[i]) | |
282 | return (body, dschead, dsc_list) | |
283 | ||
284 | ||
3e170ce0 | 285 | @header("{: <20s} {: <8s} {: <20s} {: <10s} {: <20s}".format("kmsgheader", "size", "body", "ds_count", "dsc_head")) |
d9a64523 | 286 | def GetKMsgComplexBodyDesc(kmsgp, prefix_str=""): |
3e170ce0 A |
287 | """ Routine that prints a complex kmsg's body |
288 | """ | |
289 | kmsghp = kmsgp.ikm_header | |
290 | kmsgh = dereference(kmsghp) | |
d9a64523 A |
291 | if not (kmsgh.msgh_bits & 0x80000000): |
292 | return "" | |
3e170ce0 A |
293 | format_string = "{: <#020x} {: <#08x} {: <#020x} {: <#010x} {: <#020x}" |
294 | out_string = "" | |
3e170ce0 | 295 | |
d9a64523 A |
296 | (body, dschead, dsc_list) = GetKmsgDescriptors(kmsgp) |
297 | out_string += format_string.format(kmsghp, sizeof(dereference(kmsghp)), body, len(dsc_list), dschead) | |
298 | for dsc in dsc_list: | |
299 | try: | |
300 | dsc_type = unsigned(dsc.type.type) | |
301 | out_string += "\n" + prefix_str + "Descriptor: " + xnudefines.mach_msg_type_descriptor_strings[dsc_type] | |
302 | if dsc_type == 0: | |
303 | # its a port. | |
304 | p = dsc.port.name | |
305 | dstr = GetPortDispositionString(dsc.port.disposition) | |
306 | out_string += " disp:{:s}, name:{: <#20x}".format(dstr, p) | |
307 | elif unsigned(dsc.type.type) in (1,3): | |
308 | # its OOL DESCRIPTOR or OOL VOLATILE DESCRIPTOR | |
309 | ool = dsc.out_of_line | |
310 | out_string += " " + GetMachMsgOOLDescriptorSummary(addressof(ool)) | |
311 | except: | |
312 | out_string += "\n" + prefix_str + "Invalid Descriptor: {}".format(dsc) | |
3e170ce0 A |
313 | return out_string |
314 | ||
39236c6e A |
315 | def GetKMsgSrc(kmsgp): |
316 | """ Routine that prints a kmsg's source process and pid details | |
317 | params: | |
318 | kmsgp : core.value representing the given ipc_kmsg_t struct | |
319 | returns: | |
320 | str : string containing the name and pid of the kmsg's source proc | |
321 | """ | |
322 | kmsgsrchp = Cast(kmsgp, 'ipc_kmsg_t').ikm_header | |
323 | kmsgpid = int(Cast(kern.GetValueFromAddress(unsigned(kmsgsrchp) + kmsgsrchp.msgh_size, 'uint *')[10], 'pid_t')) | |
324 | ||
325 | return "{0:s} ({1:d})".format(GetProcNameForPid(kmsgpid), kmsgpid) | |
326 | ||
3e170ce0 A |
327 | |
328 | def PrintPortSetMembers(space, setid, show_kmsg_summary): | |
329 | """ Print out the members of a given IPC PSet | |
330 | """ | |
331 | num_entries = int(space.is_table_size) | |
332 | is_tableval = space.is_table | |
333 | setid_str = GetWaitqSetidString(setid) | |
334 | ||
335 | prefix_str = "{0:<21s}".format(' '*21) | |
336 | once = True | |
337 | verbose = False | |
338 | if config['verbosity'] > vHUMAN: | |
339 | verbose = True | |
340 | ||
341 | idx = 0 | |
342 | while idx < num_entries: | |
343 | entryval = GetObjectAtIndexFromArray(is_tableval, idx) | |
344 | ie_bits = unsigned(entryval.ie_bits) | |
345 | if not (ie_bits & 0x00180000): | |
346 | # It's a port entry that's _not_ dead | |
347 | portval = Cast(entryval.ie_object, 'ipc_port_t') | |
348 | waitq = addressof(portval.ip_messages.data.port.waitq) | |
349 | psets = GetWaitqSets(addressof(portval.ip_messages.data.port.waitq)) | |
350 | for ps in psets: | |
351 | if ps == setid_str: | |
352 | if once: | |
353 | once = False | |
354 | print "{:s}\n{:s}{:s}".format(GetPortDestProc(portval), prefix_str, PrintPortSummary.header) | |
355 | PrintPortSummary(portval, show_kmsg_summary, prefix_str) | |
356 | if verbose: | |
357 | sys.stderr.write('{:d}/{:d}... \r'.format(idx, num_entries)) | |
358 | idx += 1 | |
359 | return | |
360 | ||
5ba3f43e A |
361 | def FindEntryName(obj, space): |
362 | """ Routine to locate a port/ipc_object in an ipc_space | |
363 | and return the name within that space. | |
364 | """ | |
365 | if space == 0: | |
366 | return 0 | |
367 | ||
368 | num_entries = int(space.is_table_size) | |
369 | is_tableval = space.is_table | |
370 | idx = 0 | |
371 | while idx < num_entries: | |
372 | entry_val = GetObjectAtIndexFromArray(is_tableval, idx) | |
373 | entry_bits= unsigned(entry_val.ie_bits) | |
374 | entry_obj = 0 | |
375 | if (int(entry_bits) & 0x001f0000) != 0: ## it's a valid entry | |
376 | entry_obj = unsigned(entry_val.ie_object) | |
377 | if entry_obj == unsigned(obj): | |
378 | nm = (idx << 8) | (entry_bits >> 24) | |
379 | return nm | |
380 | idx += 1 | |
381 | return 0 | |
382 | ||
3e170ce0 | 383 | |
39236c6e A |
384 | @header("{0: <20s} {1: <28s} {2: <12s} {3: <6s} {4: <6s} {5: <20s} {6: <7s}\n".format( |
385 | "portset", "waitqueue", "recvname", "flags", "refs", "recvname", "process")) | |
3e170ce0 | 386 | def PrintPortSetSummary(pset, space = 0): |
39236c6e A |
387 | """ Display summary for a given struct ipc_pset * |
388 | params: | |
389 | pset : core.value representing a pset in the kernel | |
390 | returns: | |
391 | str : string of summary information for the given pset | |
392 | """ | |
393 | out_str = "" | |
3e170ce0 A |
394 | show_kmsg_summary = False |
395 | if config['verbosity'] > vHUMAN : | |
396 | show_kmsg_summary = True | |
397 | ||
5ba3f43e | 398 | local_name = FindEntryName(pset, space) |
3e170ce0 | 399 | setid = 0 |
39236c6e | 400 | if pset.ips_object.io_bits & 0x80000000: |
3e170ce0 | 401 | setid = pset.ips_messages.data.pset.setq.wqset_id |
39236c6e A |
402 | out_str += "{0: #019x} {1: #019x} {2: <7s} {3: #011x} {4: <4s} {5: >6d} {6: #019x} ".format( |
403 | unsigned(pset), addressof(pset.ips_messages), ' '*7, | |
5ba3f43e | 404 | local_name, "ASet", |
39236c6e | 405 | pset.ips_object.io_references, |
5ba3f43e | 406 | local_name) |
39236c6e A |
407 | |
408 | else: | |
409 | out_str += "{0: #019x} {1: #019x} {2: <7s} {3: #011x} {4: <4s} {5: >6d} {6: #019x} ".format( | |
410 | unsigned(pset), addressof(pset.ips_messages), ' '*7, | |
5ba3f43e | 411 | local_name, "DSet", |
39236c6e | 412 | pset.ips_object.io_references, |
5ba3f43e | 413 | local_name) |
3e170ce0 A |
414 | print out_str |
415 | ||
416 | if setid != 0 and space != 0: | |
417 | PrintPortSetMembers(space, setid, show_kmsg_summary) | |
418 | ||
419 | return | |
39236c6e A |
420 | |
421 | # Macro: showipc | |
422 | ||
423 | @lldb_command('showipc') | |
424 | def ShowIPC(cmd_args=None): | |
425 | """ Routine to print data for the given IPC space | |
426 | Usage: showipc <address of ipc space> | |
427 | """ | |
428 | if not cmd_args: | |
429 | print "No arguments passed" | |
430 | print ShowIPC.__doc__ | |
431 | return False | |
432 | ipc = kern.GetValueFromAddress(cmd_args[0], 'ipc_space *') | |
433 | if not ipc: | |
434 | print "unknown arguments:", str(cmd_args) | |
435 | return False | |
3e170ce0 A |
436 | print PrintIPCInformation.header |
437 | PrintIPCInformation(ipc, False, False) | |
39236c6e A |
438 | |
439 | # EndMacro: showipc | |
440 | ||
441 | # Macro: showtaskipc | |
442 | ||
443 | @lldb_command('showtaskipc') | |
444 | def ShowTaskIPC(cmd_args=None): | |
445 | """ Routine to print IPC summary of given task | |
446 | Usage: showtaskipc <address of task> | |
447 | """ | |
448 | if not cmd_args: | |
449 | print "No arguments passed" | |
450 | print ShowTaskIPC.__doc__ | |
451 | return False | |
452 | tval = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
453 | if not tval: | |
454 | print "unknown arguments:", str(cmd_args) | |
455 | return False | |
456 | print GetTaskSummary.header + " " + GetProcSummary.header | |
457 | pval = Cast(tval.bsd_info, 'proc *') | |
458 | print GetTaskSummary(tval) + " " + GetProcSummary(pval) | |
d9a64523 A |
459 | print GetTaskBusyIPCSummary.header |
460 | (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tval) | |
461 | print summary | |
39236c6e A |
462 | |
463 | # EndMacro: showtaskipc | |
464 | ||
465 | # Macro: showallipc | |
466 | ||
467 | @lldb_command('showallipc') | |
468 | def ShowAllIPC(cmd_args=None): | |
469 | """ Routine to print IPC summary of all tasks | |
470 | Usage: showallipc | |
471 | """ | |
472 | for t in kern.tasks: | |
473 | print GetTaskSummary.header + " " + GetProcSummary.header | |
474 | pval = Cast(t.bsd_info, 'proc *') | |
475 | print GetTaskSummary(t) + " " + GetProcSummary(pval) | |
3e170ce0 A |
476 | print PrintIPCInformation.header |
477 | PrintIPCInformation(t.itk_space, False, False) + "\n\n" | |
39236c6e A |
478 | |
479 | # EndMacro: showallipc | |
480 | ||
481 | @lldb_command('showipcsummary') | |
482 | def ShowIPCSummary(cmd_args=None): | |
483 | """ Summarizes the IPC state of all tasks. | |
484 | This is a convenient way to dump some basic clues about IPC messaging. You can use the output to determine | |
485 | tasks that are candidates for further investigation. | |
486 | """ | |
487 | print GetTaskIPCSummary.header | |
d9a64523 | 488 | ipc_table_size = 0 |
39236c6e | 489 | for t in kern.tasks: |
d9a64523 A |
490 | (summary, table_size) = GetTaskIPCSummary(t) |
491 | ipc_table_size += table_size | |
492 | print summary | |
493 | for t in kern.terminated_tasks: | |
494 | (summary, table_size) = GetTaskIPCSummary(t) | |
495 | ipc_table_size += table_size | |
496 | print "Total Table size: {:d}".format(ipc_table_size) | |
39236c6e A |
497 | return |
498 | ||
499 | def GetKObjectFromPort(portval): | |
500 | """ Get Kobject description from the port. | |
501 | params: portval - core.value representation of 'ipc_port *' object | |
502 | returns: str - string of kobject information | |
503 | """ | |
504 | kobject_str = "{0: <#020x}".format(portval.kdata.kobject) | |
505 | io_bits = unsigned(portval.ip_object.io_bits) | |
506 | objtype_index = io_bits & 0xfff | |
507 | if objtype_index < len(xnudefines.kobject_types) : | |
39037602 A |
508 | objtype_str = xnudefines.kobject_types[objtype_index] |
509 | if objtype_str == 'IOKIT_OBJ': | |
510 | iokit_classnm = GetObjectTypeStr(portval.kdata.kobject) | |
511 | if not iokit_classnm: | |
512 | iokit_classnm = "<unknown class>" | |
513 | else: | |
514 | iokit_classnm = re.sub(r'vtable for ', r'', iokit_classnm) | |
515 | desc_str = "kobject({:s}:{:s})".format(objtype_str, iokit_classnm) | |
516 | else: | |
517 | desc_str = "kobject({0:s})".format(objtype_str) | |
518 | if xnudefines.kobject_types[objtype_index] in ('TASK_RESUME', 'TASK'): | |
519 | desc_str += " " + GetProcNameForTask(Cast(portval.kdata.kobject, 'task *')) | |
39236c6e A |
520 | else: |
521 | desc_str = "kobject(UNKNOWN) {:d}".format(objtype_index) | |
522 | return kobject_str + " " + desc_str | |
523 | ||
524 | @static_var('destcache', {}) | |
525 | def GetDestinationProcessFromPort(port): | |
526 | """ | |
527 | params: port - core.value representation of 'ipc_port *' object | |
528 | returns: str - name of process | |
529 | """ | |
530 | out_str = '' | |
531 | dest_space = port.data.receiver | |
532 | found_dest = False | |
533 | #update destcache if data is not found | |
534 | if hex(dest_space) not in GetDestinationProcessFromPort.destcache: | |
535 | for t in kern.tasks: | |
536 | if hex(t.itk_space) == hex(dest_space): | |
537 | pval = Cast(t.bsd_info, 'proc *') | |
538 | GetDestinationProcessFromPort.destcache[hex(dest_space)] = (t, pval) | |
539 | found_dest = True | |
540 | break | |
541 | #end of for loop | |
542 | else: found_dest = True | |
543 | ||
544 | if found_dest: | |
545 | (ftask , fproc) = GetDestinationProcessFromPort.destcache[hex(dest_space)] | |
546 | if fproc: | |
547 | out_str = "{0:s}({1:d})".format(fproc.p_comm, fproc.p_pid ) | |
548 | else: | |
549 | out_str = "task {0: <#020x}".format(ftask) | |
550 | return out_str | |
551 | ||
552 | ||
553 | ||
554 | @header("{0: <20s} {1: <20s}".format("destname", "destination") ) | |
555 | def GetPortDestinationSummary(port): | |
556 | """ Get destination information for a port. | |
557 | params: port - core.value representation of 'ipc_port *' object | |
558 | returns: str - string of info about ports destination | |
559 | """ | |
560 | out_str = '' | |
561 | format_string = "{0: <20s} {1: <20s}" | |
562 | destname_str = '' | |
563 | destination_str = '' | |
564 | ipc_space_kernel = unsigned(kern.globals.ipc_space_kernel) | |
565 | target_spaceval = port.data.receiver | |
566 | if unsigned(target_spaceval) == ipc_space_kernel : | |
567 | destname_str = GetKObjectFromPort(port) | |
568 | else: | |
569 | if int(port.ip_object.io_bits) & 0x80000000 : | |
570 | destname_str = "{0: <#020x}".format(port.ip_messages.data.port.receiver_name) | |
571 | destination_str = GetDestinationProcessFromPort(port) | |
572 | else: | |
573 | destname_str = "{0: <#020x}".format(port) | |
574 | destination_str = "inactive-port" | |
575 | ||
576 | out_str += format_string.format(destname_str, destination_str) | |
577 | return out_str | |
578 | ||
579 | @lldb_type_summary(['ipc_entry_t']) | |
d9a64523 | 580 | @header("{: <20s} {: <12s} {: <8s} {: <8s} {: <8s} {: <8s} {: <20s} {: <20s}".format("object", "name", "rite", "urefs", "nsets", "nmsgs", "destname", "destination")) |
3e170ce0 | 581 | def GetIPCEntrySummary(entry, ipc_name='', rights_filter=0): |
39236c6e A |
582 | """ Get summary of a ipc entry. |
583 | params: | |
584 | entry - core.value representing ipc_entry_t in the kernel | |
585 | ipc_name - str of format '0x0123' for display in summary. | |
586 | returns: | |
587 | str - string of ipc entry related information | |
3e170ce0 A |
588 | |
589 | types of rights: | |
590 | 'Dead' : Dead name | |
591 | 'Set' : Port set | |
592 | 'S' : Send right | |
593 | 'R' : Receive right | |
594 | 'O' : Send-once right | |
595 | types of notifications: | |
d9a64523 | 596 | 'd' : Dead-Name notification requested |
3e170ce0 | 597 | 's' : Send-Possible notification armed |
d9a64523 A |
598 | 'r' : Send-Possible notification requested |
599 | 'n' : No-Senders notification requested | |
600 | 'x' : Port-destroy notification requested | |
39236c6e | 601 | """ |
d9a64523 | 602 | out_str = '' |
39236c6e | 603 | entry_ptr = int(hex(entry), 16) |
3e170ce0 | 604 | format_string = "{: <#020x} {: <12s} {: <8s} {: <8d} {: <8d} {: <8d} {: <20s} {: <20s}" |
39236c6e A |
605 | right_str = '' |
606 | destname_str = '' | |
607 | destination_str = '' | |
d9a64523 | 608 | |
39236c6e A |
609 | ie_object = entry.ie_object |
610 | ie_bits = int(entry.ie_bits) | |
611 | urefs = int(ie_bits & 0xffff) | |
3e170ce0 A |
612 | nsets = 0 |
613 | nmsgs = 0 | |
39236c6e A |
614 | if ie_bits & 0x00100000 : |
615 | right_str = 'Dead' | |
616 | elif ie_bits & 0x00080000: | |
617 | right_str = 'Set' | |
3e170ce0 A |
618 | psetval = Cast(ie_object, 'ipc_pset *') |
619 | set_str = GetWaitqSets(addressof(psetval.ips_messages.data.pset.setq.wqset_q)) | |
620 | nsets = len(set_str) | |
621 | nmsgs = 0 | |
39236c6e A |
622 | else: |
623 | if ie_bits & 0x00010000 : | |
624 | if ie_bits & 0x00020000 : | |
3e170ce0 | 625 | # SEND + RECV |
39236c6e A |
626 | right_str = 'SR' |
627 | else: | |
3e170ce0 | 628 | # SEND only |
39236c6e A |
629 | right_str = 'S' |
630 | elif ie_bits & 0x00020000: | |
3e170ce0 | 631 | # RECV only |
39236c6e A |
632 | right_str = 'R' |
633 | elif ie_bits & 0x00040000 : | |
3e170ce0 | 634 | # SEND_ONCE |
39236c6e | 635 | right_str = 'O' |
fe8ab488 | 636 | portval = Cast(ie_object, 'ipc_port_t') |
39236c6e | 637 | if int(entry.index.request) != 0: |
39236c6e A |
638 | requestsval = portval.ip_requests |
639 | sorightval = requestsval[int(entry.index.request)].notify.port | |
640 | soright_ptr = unsigned(sorightval) | |
641 | if soright_ptr != 0: | |
d9a64523 A |
642 | # dead-name notification requested |
643 | right_str += 'd' | |
644 | # send-possible armed | |
645 | if soright_ptr & 0x1 : right_str +='s' | |
646 | # send-possible requested | |
647 | if soright_ptr & 0x2 : right_str +='r' | |
3e170ce0 | 648 | # No-senders notification requested |
d9a64523 A |
649 | if portval.ip_nsrequest != 0: right_str += 'n' |
650 | # port-destroy notification requested | |
651 | if portval.ip_pdrequest != 0: right_str += 'x' | |
652 | ||
653 | # early-out if the rights-filter doesn't match | |
654 | if rights_filter != 0 and rights_filter != right_str: | |
655 | return '' | |
656 | ||
657 | # append the generation to the name value | |
658 | # (from osfmk/ipc/ipc_entry.h) | |
659 | # bits rollover period | |
660 | # 0 0 64 | |
661 | # 0 1 48 | |
662 | # 1 0 32 | |
663 | # 1 1 16 | |
664 | ie_gen_roll = { 0:'.64', 1:'.48', 2:'.32', 3:'.16' } | |
665 | ipc_name = '{:s}{:s}'.format(strip(ipc_name), ie_gen_roll[(ie_bits & 0x00c00000) >> 22]) | |
666 | ||
39236c6e A |
667 | # now show the port destination part |
668 | destname_str = GetPortDestinationSummary(Cast(ie_object, 'ipc_port_t')) | |
3e170ce0 A |
669 | # Get the number of sets to which this port belongs |
670 | set_str = GetWaitqSets(addressof(portval.ip_messages.data.port.waitq)) | |
671 | nsets = len(set_str) | |
672 | nmsgs = portval.ip_messages.data.port.msgcount | |
673 | if rights_filter == 0 or rights_filter == right_str: | |
674 | out_str = format_string.format(ie_object, ipc_name, right_str, urefs, nsets, nmsgs, destname_str, destination_str) | |
39236c6e A |
675 | return out_str |
676 | ||
677 | @header("{0: >20s}".format("user bt") ) | |
678 | def GetPortUserStack(port, task): | |
679 | """ Get UserStack information for the given port & task. | |
680 | params: port - core.value representation of 'ipc_port *' object | |
681 | task - value representing 'task *' object | |
682 | returns: str - string information on port's userstack | |
683 | """ | |
684 | out_str = '' | |
685 | ie_port_callstack = port.ip_callstack | |
686 | ie_port_spares = port.ip_spares[0] | |
687 | proc_val = Cast(task.bsd_info, 'proc *') | |
688 | if ie_port_callstack[0]: | |
689 | out_str += "{: <10x}".format(ie_port_callstack[0]) | |
690 | count = 1 | |
691 | while count < 16 and ie_port_callstack[count]: | |
692 | out_str += ": <10x".format(ie_port_callstack[count]) | |
693 | count = count + 1 | |
694 | if ie_port_spares != proc_val.p_pid: | |
695 | out_str += " ({:<10d})".format(ie_port_spares) | |
696 | out_str += '\n' | |
697 | return out_str | |
698 | ||
699 | @lldb_type_summary(['ipc_space *']) | |
3e170ce0 A |
700 | @header("{0: <20s} {1: <20s} {2: <20s} {3: <8s} {4: <10s} {5: <18s} {6: >8s} {7: <8s}".format('ipc_space', 'is_task', 'is_table', 'flags', 'ports', 'table_next', 'low_mod', 'high_mod')) |
701 | def PrintIPCInformation(space, show_entries=False, show_userstack=False, rights_filter=0): | |
39236c6e A |
702 | """ Provide a summary of the ipc space |
703 | """ | |
704 | out_str = '' | |
3e170ce0 | 705 | format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: <8s} {4: <10d} {5: <#18x} {6: >8d} {7: <8d}" |
39236c6e A |
706 | is_tableval = space.is_table |
707 | ports = int(space.is_table_size) | |
708 | flags ='' | |
709 | is_bits = int(space.is_bits) | |
710 | if (is_bits & 0x40000000) == 0: flags +='A' | |
711 | else: flags += ' ' | |
712 | if (is_bits & 0x20000000) != 0: flags +='G' | |
3e170ce0 | 713 | print format_string.format(space, space.is_task, space.is_table, flags, space.is_table_size, space.is_table_next, space.is_low_mod, space.is_high_mod) |
39236c6e A |
714 | |
715 | #should show the each individual entries if asked. | |
716 | if show_entries == True: | |
3e170ce0 | 717 | print "\t" + GetIPCEntrySummary.header |
39236c6e A |
718 | num_entries = ports |
719 | index = 0 | |
720 | while index < num_entries: | |
721 | entryval = GetObjectAtIndexFromArray(is_tableval, index) | |
722 | entry_ie_bits = unsigned(entryval.ie_bits) | |
723 | if (int(entry_ie_bits) & 0x001f0000 ) != 0: | |
724 | entry_name = "{0: <#020x}".format( (index <<8 | entry_ie_bits >> 24) ) | |
3e170ce0 A |
725 | entry_str = GetIPCEntrySummary(entryval, entry_name, rights_filter) |
726 | if len(entry_str) > 0: | |
727 | print " \r\t" + entry_str | |
728 | if show_userstack == True: | |
729 | entryport = Cast(entryval.ie_object, 'ipc_port *') | |
730 | if entryval.ie_object and (int(entry_ie_bits) & 0x00070000) and entryport.ip_callstack[0]: | |
731 | print GetPortUserStack.header + GetPortUserStack(entryport, space.is_task) | |
732 | else: | |
733 | # give some progress indication (this is especially | |
734 | # helpful for tasks with large sets of rights) | |
735 | sys.stderr.write(' {:d}/{:d}...\r'.format(index, num_entries)) | |
736 | index += 1 | |
39236c6e A |
737 | #done with showing entries |
738 | return out_str | |
739 | ||
740 | # Macro: showrights | |
741 | ||
3e170ce0 A |
742 | @lldb_command('showrights', 'R:') |
743 | def ShowRights(cmd_args=None, cmd_options={}): | |
39236c6e | 744 | """ Routine to print rights information for the given IPC space |
3e170ce0 A |
745 | Usage: showrights [-R rights_type] <address of ipc space> |
746 | -R rights_type : only display rights matching the string 'rights_type' | |
747 | ||
748 | types of rights: | |
749 | 'Dead' : Dead name | |
750 | 'Set' : Port set | |
751 | 'S' : Send right | |
752 | 'R' : Receive right | |
753 | 'O' : Send-once right | |
d9a64523 A |
754 | types of notifications: |
755 | 'd' : Dead-Name notification requested | |
3e170ce0 | 756 | 's' : Send-Possible notification armed |
d9a64523 A |
757 | 'r' : Send-Possible notification requested |
758 | 'n' : No-Senders notification requested | |
759 | 'x' : Port-destroy notification requested | |
39236c6e A |
760 | """ |
761 | if not cmd_args: | |
762 | print "No arguments passed" | |
763 | print ShowRights.__doc__ | |
764 | return False | |
765 | ipc = kern.GetValueFromAddress(cmd_args[0], 'ipc_space *') | |
766 | if not ipc: | |
767 | print "unknown arguments:", str(cmd_args) | |
768 | return False | |
3e170ce0 A |
769 | rights_type = 0 |
770 | if "-R" in cmd_options: | |
771 | rights_type = cmd_options["-R"] | |
772 | print PrintIPCInformation.header | |
773 | PrintIPCInformation(ipc, True, False, rights_type) | |
39236c6e A |
774 | |
775 | # EndMacro: showrights | |
776 | ||
3e170ce0 A |
777 | @lldb_command('showtaskrights','R:') |
778 | def ShowTaskRights(cmd_args=None, cmd_options={}): | |
39236c6e | 779 | """ Routine to ipc rights information for a task |
3e170ce0 A |
780 | Usage: showtaskrights [-R rights_type] <task address> |
781 | -R rights_type : only display rights matching the string 'rights_type' | |
782 | ||
783 | types of rights: | |
784 | 'Dead' : Dead name | |
785 | 'Set' : Port set | |
786 | 'S' : Send right | |
787 | 'R' : Receive right | |
788 | 'O' : Send-once right | |
d9a64523 A |
789 | types of notifications: |
790 | 'd' : Dead-Name notification requested | |
3e170ce0 | 791 | 's' : Send-Possible notification armed |
d9a64523 A |
792 | 'r' : Send-Possible notification requested |
793 | 'n' : No-Senders notification requested | |
794 | 'x' : Port-destroy notification requested | |
39236c6e A |
795 | """ |
796 | if cmd_args == None: | |
797 | print "No arguments passed" | |
798 | print ShowTaskStacksCmdHelper.__doc__ | |
799 | return False | |
800 | tval = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
801 | if not tval: | |
802 | print "unknown arguments:", str(cmd_args) | |
803 | return False | |
3e170ce0 A |
804 | rights_type = 0 |
805 | if "-R" in cmd_options: | |
806 | rights_type = cmd_options["-R"] | |
39236c6e A |
807 | print GetTaskSummary.header + " " + GetProcSummary.header |
808 | pval = Cast(tval.bsd_info, 'proc *') | |
809 | print GetTaskSummary(tval) + " " + GetProcSummary(pval) | |
3e170ce0 A |
810 | print PrintIPCInformation.header |
811 | PrintIPCInformation(tval.itk_space, True, False, rights_type) | |
39236c6e A |
812 | |
813 | # Macro: showataskrightsbt | |
814 | ||
3e170ce0 A |
815 | @lldb_command('showtaskrightsbt', 'R:') |
816 | def ShowTaskRightsBt(cmd_args=None, cmd_options={}): | |
39236c6e | 817 | """ Routine to ipc rights information with userstacks for a task |
3e170ce0 A |
818 | Usage: showtaskrightsbt [-R rights_type] <task address> |
819 | -R rights_type : only display rights matching the string 'rights_type' | |
820 | ||
821 | types of rights: | |
822 | 'Dead' : Dead name | |
823 | 'Set' : Port set | |
824 | 'S' : Send right | |
825 | 'R' : Receive right | |
826 | 'O' : Send-once right | |
d9a64523 A |
827 | types of notifications: |
828 | 'd' : Dead-Name notification requested | |
3e170ce0 | 829 | 's' : Send-Possible notification armed |
d9a64523 A |
830 | 'r' : Send-Possible notification requested |
831 | 'n' : No-Senders notification requested | |
832 | 'x' : Port-destroy notification requested | |
39236c6e A |
833 | """ |
834 | if cmd_args == None: | |
835 | print "No arguments passed" | |
836 | print ShowTaskRightsBt.__doc__ | |
837 | return False | |
838 | tval = kern.GetValueFromAddress(cmd_args[0], 'task *') | |
839 | if not tval: | |
840 | print "unknown arguments:", str(cmd_args) | |
841 | return False | |
3e170ce0 A |
842 | rights_type = 0 |
843 | if "-R" in cmd_options: | |
844 | rights_type = cmd_options["-R"] | |
39236c6e A |
845 | print GetTaskSummary.header + " " + GetProcSummary.header |
846 | pval = Cast(tval.bsd_info, 'proc *') | |
847 | print GetTaskSummary(tval) + " " + GetProcSummary(pval) | |
3e170ce0 A |
848 | print PrintIPCInformation.header |
849 | PrintIPCInformation(tval.itk_space, True, True, rights_type) | |
39236c6e A |
850 | |
851 | # EndMacro: showtaskrightsbt | |
852 | ||
853 | # Macro: showallrights | |
854 | ||
3e170ce0 A |
855 | @lldb_command('showallrights', 'R:') |
856 | def ShowAllRights(cmd_args=None, cmd_options={}): | |
39236c6e | 857 | """ Routine to print rights information for IPC space of all tasks |
3e170ce0 A |
858 | Usage: showallrights [-R rights_type] |
859 | -R rights_type : only display rights matching the string 'rights_type' | |
860 | ||
861 | types of rights: | |
862 | 'Dead' : Dead name | |
863 | 'Set' : Port set | |
864 | 'S' : Send right | |
865 | 'R' : Receive right | |
866 | 'O' : Send-once right | |
d9a64523 A |
867 | types of notifications: |
868 | 'd' : Dead-Name notification requested | |
3e170ce0 | 869 | 's' : Send-Possible notification armed |
d9a64523 A |
870 | 'r' : Send-Possible notification requested |
871 | 'n' : No-Senders notification requested | |
872 | 'x' : Port-destroy notification requested | |
39236c6e | 873 | """ |
3e170ce0 A |
874 | rights_type = 0 |
875 | if "-R" in cmd_options: | |
876 | rights_type = cmd_options["-R"] | |
39236c6e A |
877 | for t in kern.tasks: |
878 | print GetTaskSummary.header + " " + GetProcSummary.header | |
879 | pval = Cast(t.bsd_info, 'proc *') | |
880 | print GetTaskSummary(t) + " " + GetProcSummary(pval) | |
fe8ab488 | 881 | try: |
3e170ce0 A |
882 | print PrintIPCInformation.header |
883 | PrintIPCInformation(t.itk_space, True, False, rights_type) + "\n\n" | |
fe8ab488 A |
884 | except (KeyboardInterrupt, SystemExit): |
885 | raise | |
886 | except: | |
887 | print "Failed to get IPC information. Do individual showtaskrights <task> to find the error. \n\n" | |
39236c6e A |
888 | |
889 | # EndMacro: showallrights | |
890 | ||
d9a64523 A |
891 | |
892 | def GetInTransitPortSummary(port, disp, holding_port, holding_kmsg): | |
893 | """ String-ify the in-transit dispostion of a port. | |
894 | """ | |
895 | ## This should match the summary generated by GetIPCEntrySummary | |
896 | ## "object" "name" "rite" "urefs" "nsets" "nmsgs" "destname" "destination" | |
897 | format_str = "\t{: <#20x} {: <12} {: <8s} {: <8d} {: <8d} {: <8d} p:{: <#19x} k:{: <#19x}" | |
898 | portname = 'intransit' | |
899 | ||
900 | disp_str = GetPortDispositionString(disp) | |
901 | ||
902 | out_str = format_str.format(unsigned(port), 'in-transit', disp_str, 0, 0, port.ip_messages.data.port.msgcount, unsigned(holding_port), unsigned(holding_kmsg)) | |
903 | return out_str | |
904 | ||
905 | ||
906 | def GetDispositionFromEntryType(entry_bits): | |
907 | """ Translate an IPC entry type into an in-transit disposition. This allows | |
908 | the GetInTransitPortSummary function to be re-used to string-ify IPC | |
909 | entry types. | |
910 | """ | |
911 | ebits = int(entry_bits) | |
912 | if (ebits & 0x003f0000) == 0: | |
913 | return 0 | |
914 | ||
915 | if (ebits & 0x00010000) != 0: | |
916 | return 17 ## MACH_PORT_RIGHT_SEND | |
917 | elif (ebits & 0x00020000) != 0: | |
918 | return 16 ## MACH_PORT_RIGHT_RECEIVE | |
919 | elif (ebits & 0x00040000) != 0: | |
920 | return 18 ## MACH_PORT_RIGHT_SEND_ONCE | |
921 | elif (ebits & 0x00080000) != 0: | |
922 | return 100 ## MACH_PORT_RIGHT_PORT_SET | |
923 | elif (ebits & 0x00100000) != 0: | |
924 | return 101 ## MACH_PORT_RIGHT_DEAD_NAME | |
925 | elif (ebits & 0x00200000) != 0: | |
926 | return 102 ## MACH_PORT_RIGHT_LABELH | |
927 | else: | |
928 | return 0 | |
929 | ||
930 | def GetDispositionFromVoucherPort(th_vport): | |
931 | """ Translate a thread's voucher port into a 'disposition' | |
932 | """ | |
933 | if unsigned(th_vport) > 0: | |
934 | return 103 ## Voucher type | |
935 | return 0 | |
936 | ||
937 | ||
938 | g_kmsg_prog = 0 | |
939 | g_progmeter = { | |
940 | 0 : '*', | |
941 | 1 : '-', | |
942 | 2 : '\\', | |
943 | 3 : '|', | |
944 | 4 : '/', | |
945 | 5 : '-', | |
946 | 6 : '\\', | |
947 | 7 : '|', | |
948 | 8 : '/', | |
949 | } | |
950 | ||
951 | def PrintProgressForKmsg(): | |
952 | global g_kmsg_prog | |
953 | global g_progmeter | |
954 | sys.stderr.write(" {:<1s}\r".format(g_progmeter[g_kmsg_prog % 9])) | |
955 | g_kmsg_prog += 1 | |
956 | ||
957 | ||
958 | def CollectPortsForAnalysis(port, disposition): | |
959 | """ | |
960 | """ | |
961 | p = Cast(port, 'struct ipc_port *') | |
962 | yield (p, disposition) | |
963 | ||
964 | # no-senders notification port | |
965 | if unsigned(p.ip_nsrequest) != 0: | |
966 | PrintProgressForKmsg() | |
967 | yield (Cast(p.ip_nsrequest, 'struct ipc_port *'), -1) | |
968 | ||
969 | # port-death notification port | |
970 | if unsigned(p.ip_pdrequest) != 0: | |
971 | PrintProgressForKmsg() | |
972 | yield (Cast(p.ip_pdrequest, 'struct ipc_port *'), -2) | |
973 | ||
974 | ## ports can have many send-possible notifications armed: go through the table! | |
975 | if unsigned(p.ip_requests) != 0: | |
976 | table = Cast(p.ip_requests, 'struct ipc_port_request *') | |
977 | table_sz = int(table.name.size.its_size) | |
978 | for i in range(table_sz): | |
979 | if i == 0: | |
980 | continue | |
981 | ipr = table[i] | |
982 | if unsigned(ipr.name.name) != 0: | |
983 | ipr_bits = unsigned(ipr.notify.port) & 3 | |
984 | ipr_port = kern.GetValueFromAddress(int(ipr.notify.port) & ~3, 'struct ipc_port *') | |
985 | ipr_disp = 0 | |
986 | if ipr_bits & 3: ## send-possible armed and requested | |
987 | ipr_disp = -5 | |
988 | elif ipr_bits & 2: ## send-possible requested | |
989 | ipr_disp = -4 | |
990 | elif ipr_bits & 1: ## send-possible armed | |
991 | ipr_disp = -3 | |
992 | PrintProgressForKmsg() | |
993 | yield (ipr_port, ipr_disp) | |
994 | return | |
995 | ||
996 | def CollectKmsgPorts(task, task_port, kmsgp): | |
997 | """ Look through a message, 'kmsgp' destined for 'task' | |
998 | (enqueued on task_port). Collect any port descriptors, | |
999 | remote, local, voucher, or other port references | |
1000 | into a (ipc_port_t, disposition) list. | |
1001 | """ | |
1002 | kmsgh = dereference(kmsgp.ikm_header) | |
1003 | ||
1004 | p_list = [] | |
1005 | ||
1006 | PrintProgressForKmsg() | |
1007 | if kmsgh.msgh_remote_port and unsigned(kmsgh.msgh_remote_port) != unsigned(task_port): | |
1008 | disp = kmsgh.msgh_bits & 0x1f | |
1009 | p_list += list(CollectPortsForAnalysis(kmsgh.msgh_remote_port, disp)) | |
1010 | ||
1011 | if kmsgh.msgh_local_port and unsigned(kmsgh.msgh_local_port) != unsigned(task_port) \ | |
1012 | and unsigned(kmsgh.msgh_local_port) != unsigned(kmsgh.msgh_remote_port): | |
1013 | disp = (kmsgh.msgh_bits & 0x1f00) >> 8 | |
1014 | p_list += list(CollectPortsForAnalysis(kmsgh.msgh_local_port, disp)) | |
1015 | ||
1016 | if kmsgp.ikm_voucher: | |
1017 | p_list += list(CollectPortsForAnalysis(kmsgp.ikm_voucher, 0)) | |
1018 | ||
1019 | if kmsgh.msgh_bits & 0x80000000: | |
1020 | ## Complex message - look for descriptors | |
1021 | PrintProgressForKmsg() | |
1022 | (body, dschead, dsc_list) = GetKmsgDescriptors(kmsgp) | |
1023 | for dsc in dsc_list: | |
1024 | PrintProgressForKmsg() | |
1025 | dsc_type = unsigned(dsc.type.type) | |
1026 | if dsc_type == 0 or dsc_type == 2: ## 0 == port, 2 == ool port | |
1027 | if dsc_type == 0: | |
1028 | ## its a port descriptor | |
1029 | dsc_disp = dsc.port.disposition | |
1030 | p_list += list(CollectPortsForAnalysis(dsc.port.name, dsc_disp)) | |
1031 | else: | |
1032 | ## it's an ool_ports descriptor which is an array of ports | |
1033 | dsc_disp = dsc.ool_ports.disposition | |
1034 | dispdata = Cast(dsc.ool_ports.address, 'struct ipc_port *') | |
1035 | for pidx in range(dsc.ool_ports.count): | |
1036 | PrintProgressForKmsg() | |
1037 | p_list += list(CollectPortsForAnalysis(dispdata[pidx], dsc_disp)) | |
1038 | return p_list | |
1039 | ||
1040 | def CollectKmsgPortRefs(task, task_port, kmsgp, p_refs): | |
1041 | """ Recursively collect all references to ports inside the kmsg 'kmsgp' | |
1042 | into the set 'p_refs' | |
1043 | """ | |
1044 | p_list = CollectKmsgPorts(task, task_port, kmsgp) | |
1045 | ||
1046 | ## Iterate over each ports we've collected, to see if they | |
1047 | ## have messages on them, and then recurse! | |
1048 | for p, pdisp in p_list: | |
1049 | ptype = (p.ip_object.io_bits & 0x7fff0000) >> 16 | |
1050 | p_refs.add((p, pdisp, ptype)) | |
1051 | if ptype != 0: ## don't bother with port sets | |
1052 | continue | |
1053 | ## If the port that's in-transit has messages already enqueued, | |
1054 | ## go through each of those messages and look for more ports! | |
1055 | if p.ip_messages.data.port.msgcount > 0: | |
1056 | p_kmsgp = Cast(p.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t') | |
1057 | kmsgheadp = p_kmsgp | |
1058 | while unsigned(p_kmsgp) > 0: | |
1059 | CollectKmsgPortRefs(task, p, p_kmsgp, p_refs) | |
1060 | p_kmsgp = p_kmsgp.ikm_next | |
1061 | if p_kmsgp == kmsgheadp: | |
1062 | break; | |
1063 | ||
1064 | ||
1065 | def FindKmsgPortRefs(instr, task, task_port, kmsgp, qport): | |
1066 | """ Look through a message, 'kmsgp' destined for 'task'. If we find | |
1067 | any port descriptors, remote, local, voucher, or other port that | |
1068 | matches 'qport', return a short description | |
1069 | which should match the format of GetIPCEntrySummary. | |
1070 | """ | |
1071 | ||
1072 | out_str = instr | |
1073 | p_list = CollectKmsgPorts(task, task_port, kmsgp) | |
1074 | ||
1075 | ## Run through all ports we've collected looking for 'qport' | |
1076 | for p, pdisp in p_list: | |
1077 | PrintProgressForKmsg() | |
1078 | if unsigned(p) == unsigned(qport): | |
1079 | ## the port we're looking for was found in this message! | |
1080 | if len(out_str) > 0: | |
1081 | out_str += '\n' | |
1082 | out_str += GetInTransitPortSummary(p, pdisp, task_port, kmsgp) | |
1083 | ||
1084 | ptype = (p.ip_object.io_bits & 0x7fff0000) >> 16 | |
1085 | if ptype != 0: ## don't bother with port sets | |
1086 | continue | |
1087 | ||
1088 | ## If the port that's in-transit has messages already enqueued, | |
1089 | ## go through each of those messages and look for more ports! | |
1090 | if p.ip_messages.data.port.msgcount > 0: | |
1091 | p_kmsgp = Cast(p.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t') | |
1092 | kmsgheadp = p_kmsgp | |
1093 | while unsigned(p_kmsgp) > 0: | |
1094 | out_str = FindKmsgPortRefs(out_str, task, p, p_kmsgp, qport) | |
1095 | p_kmsgp = p_kmsgp.ikm_next | |
1096 | if p_kmsgp == kmsgheadp: | |
1097 | break | |
1098 | return out_str | |
1099 | ||
1100 | ||
1101 | port_iteration_do_print_taskname = False | |
1102 | registeredport_idx = -10 | |
1103 | excports_idx = -20 | |
1104 | intransit_idx = -1000 | |
1105 | taskports_idx = -2000 | |
1106 | thports_idx = -3000 | |
1107 | ||
1108 | def IterateAllPorts(tasklist, func, ctx, include_psets, follow_busyports, should_log): | |
1109 | """ Iterate over all ports in the system, calling 'func' | |
1110 | for each entry in | |
1111 | """ | |
1112 | global port_iteration_do_print_taskname | |
1113 | global intransit_idx, taskports_idx, thports_idx, registeredport_idx, excports_idx | |
1114 | ||
1115 | ## XXX: also host special ports | |
1116 | ||
1117 | entry_port_type_mask = 0x00070000 | |
1118 | if include_psets: | |
1119 | entry_port_type_mask = 0x000f0000 | |
1120 | ||
1121 | if tasklist is None: | |
1122 | tasklist = kern.tasks | |
1123 | tasklist += kern.terminated_tasks | |
1124 | ||
1125 | tidx = 1 | |
1126 | ||
1127 | for t in tasklist: | |
1128 | # Write a progress line. Using stderr avoids automatic newline when | |
1129 | # writing to stdout from lldb. Blank spaces at the end clear out long | |
1130 | # lines. | |
1131 | if should_log: | |
1132 | procname = "" | |
1133 | if not t.active: | |
1134 | procname = 'terminated: ' | |
1135 | if t.halting: | |
1136 | procname += 'halting: ' | |
1137 | t_p = Cast(t.bsd_info, 'proc *') | |
1138 | if unsigned(t_p) != 0: | |
1139 | procname += str(t_p.p_name) | |
1140 | elif unsigned(t.task_imp_base) != 0 and hasattr(t.task_imp_base, 'iit_procname'): | |
1141 | procname += str(t.task_imp_base.iit_procname) | |
1142 | sys.stderr.write(" checking {:s} ({}/{})...{:50s}\r".format(procname, tidx, len(tasklist), '')) | |
1143 | tidx += 1 | |
1144 | ||
1145 | port_iteration_do_print_taskname = True | |
1146 | space = t.itk_space | |
1147 | num_entries = int(space.is_table_size) | |
1148 | is_tableval = space.is_table | |
1149 | idx = 0 | |
1150 | while idx < num_entries: | |
1151 | entry_val = GetObjectAtIndexFromArray(is_tableval, idx) | |
1152 | entry_bits= unsigned(entry_val.ie_bits) | |
1153 | entry_obj = 0 | |
1154 | entry_str = '' | |
1155 | entry_name = "{:x}".format( (idx << 8 | entry_bits >> 24) ) | |
1156 | ||
1157 | entry_disp = GetDispositionFromEntryType(entry_bits) | |
1158 | ||
1159 | ## If the entry in the table represents a port of some sort, | |
1160 | ## then make the callback provided | |
1161 | if int(entry_bits) & entry_port_type_mask: | |
1162 | eport = Cast(entry_val.ie_object, 'ipc_port_t') | |
1163 | ## Make the callback | |
1164 | func(t, space, ctx, idx, entry_val, eport, entry_disp) | |
1165 | ||
1166 | ## if the port has pending messages, look through | |
1167 | ## each message for ports (and recurse) | |
1168 | if follow_busyports and unsigned(eport) > 0 and eport.ip_messages.data.port.msgcount > 0: | |
1169 | ## collect all port references from all messages | |
1170 | kmsgp = Cast(eport.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t') | |
1171 | kmsgheadp = kmsgp | |
1172 | while unsigned(kmsgp) > 0: | |
1173 | p_refs = set() | |
1174 | CollectKmsgPortRefs(t, eport, kmsgp, p_refs) | |
1175 | for (port, pdisp, ptype) in p_refs: | |
1176 | func(t, space, ctx, intransit_idx, None, port, pdisp) | |
1177 | kmsgp = kmsgp.ikm_next | |
1178 | if kmsgp == kmsgheadp: | |
1179 | break | |
1180 | ||
1181 | idx = idx + 1 | |
1182 | ## while (idx < num_entries) | |
1183 | ||
1184 | ## Task ports (send rights) | |
1185 | if unsigned(t.itk_sself) > 0: | |
1186 | func(t, space, ctx, taskports_idx, 0, t.itk_sself, 17) | |
1187 | if unsigned(t.itk_host) > 0: | |
1188 | func(t, space, ctx, taskports_idx, 0, t.itk_host, 17) | |
1189 | if unsigned(t.itk_bootstrap) > 0: | |
1190 | func(t, space, ctx, taskports_idx, 0, t.itk_bootstrap, 17) | |
1191 | if unsigned(t.itk_seatbelt) > 0: | |
1192 | func(t, space, ctx, taskports_idx, 0, t.itk_seatbelt, 17) | |
1193 | if unsigned(t.itk_gssd) > 0: | |
1194 | func(t, space, ctx, taskports_idx, 0, t.itk_gssd, 17) | |
1195 | if unsigned(t.itk_debug_control) > 0: | |
1196 | func(t, space, ctx, taskports_idx, 0, t.itk_debug_control, 17) | |
1197 | if unsigned(t.itk_task_access) > 0: | |
1198 | func(t, space, ctx, taskports_idx, 0, t.itk_task_access, 17) | |
1199 | ||
1200 | ## Task name port (not a send right, just a naked ref) | |
1201 | if unsigned(t.itk_nself) > 0: | |
1202 | func(t, space, ctx, taskports_idx, 0,t.itk_nself, 0) | |
1203 | ||
1204 | ## task resume port is a receive right to resume the task | |
1205 | if unsigned(t.itk_resume) > 0: | |
1206 | func(t, space, ctx, taskports_idx, 0, t.itk_resume, 16) | |
1207 | ||
1208 | ## registered task ports (all send rights) | |
1209 | tr_idx = 0 | |
1210 | tr_max = sizeof(t.itk_registered) / sizeof(t.itk_registered[0]) | |
1211 | while tr_idx < tr_max: | |
1212 | tport = t.itk_registered[tr_idx] | |
1213 | if unsigned(tport) > 0: | |
1214 | try: | |
1215 | func(t, space, ctx, registeredport_idx, 0, tport, 17) | |
1216 | except Exception, e: | |
1217 | print("\texception looking through registered port {:d}/{:d} in {:s}".format(tr_idx,tr_max,t)) | |
1218 | pass | |
1219 | tr_idx += 1 | |
1220 | ||
1221 | ## Task exception ports | |
1222 | exidx = 0 | |
1223 | exmax = sizeof(t.exc_actions) / sizeof(t.exc_actions[0]) | |
1224 | while exidx < exmax: ## see: osfmk/mach/[arm|i386]/exception.h | |
1225 | export = t.exc_actions[exidx].port ## send right | |
1226 | if unsigned(export) > 0: | |
1227 | try: | |
1228 | func(t, space, ctx, excports_idx, 0, export, 17) | |
1229 | except Exception, e: | |
1230 | print("\texception looking through exception port {:d}/{:d} in {:s}".format(exidx,exmax,t)) | |
1231 | pass | |
1232 | exidx += 1 | |
1233 | ||
1234 | ## XXX: any ports still valid after clearing IPC space?! | |
1235 | ||
1236 | for thval in IterateQueue(t.threads, 'thread *', 'task_threads'): | |
1237 | ## XXX: look at block reason to see if it's in mach_msg_receive - then look at saved state / message | |
1238 | ||
1239 | ## Thread port (send right) | |
1240 | if unsigned(thval.ith_sself) > 0: | |
1241 | thport = thval.ith_sself | |
1242 | func(t, space, ctx, thports_idx, 0, thport, 17) ## see: osfmk/mach/message.h | |
1243 | ## Thread special reply port (send-once right) | |
1244 | if unsigned(thval.ith_special_reply_port) > 0: | |
1245 | thport = thval.ith_special_reply_port | |
1246 | func(t, space, ctx, thports_idx, 0, thport, 18) ## see: osfmk/mach/message.h | |
1247 | ## Thread voucher port | |
1248 | if unsigned(thval.ith_voucher) > 0: | |
1249 | vport = thval.ith_voucher.iv_port | |
1250 | if unsigned(vport) > 0: | |
1251 | vdisp = GetDispositionFromVoucherPort(vport) | |
1252 | func(t, space, ctx, thports_idx, 0, vport, vdisp) | |
1253 | ## Thread exception ports | |
1254 | if unsigned(thval.exc_actions) > 0: | |
1255 | exidx = 0 | |
1256 | while exidx < exmax: ## see: osfmk/mach/[arm|i386]/exception.h | |
1257 | export = thval.exc_actions[exidx].port ## send right | |
1258 | if unsigned(export) > 0: | |
1259 | try: | |
1260 | func(t, space, ctx, excports_idx, 0, export, 17) | |
1261 | except Exception, e: | |
1262 | print("\texception looking through exception port {:d}/{:d} in {:s}".format(exidx,exmax,t)) | |
1263 | pass | |
1264 | exidx += 1 | |
1265 | ## XXX: the message on a thread (that's currently being received) | |
1266 | ## for (thval in t.threads) | |
1267 | ## for (t in tasklist) | |
1268 | ||
1269 | ||
1270 | # Macro: findportrights | |
1271 | def FindPortRightsCallback(task, space, ctx, entry_idx, ipc_entry, ipc_port, port_disp): | |
1272 | """ Callback which uses 'ctx' as the (port,rights_types) tuple for which | |
1273 | a caller is seeking references. This should *not* be used from a | |
1274 | recursive call to IterateAllPorts. | |
1275 | """ | |
1276 | global port_iteration_do_print_taskname | |
1277 | ||
1278 | (qport, rights_type) = ctx | |
1279 | entry_name = '' | |
1280 | entry_str = '' | |
1281 | if unsigned(ipc_entry) != 0: | |
1282 | entry_bits = unsigned(ipc_entry.ie_bits) | |
1283 | entry_name = "{:x}".format( (entry_idx << 8 | entry_bits >> 24) ) | |
1284 | if (int(entry_bits) & 0x001f0000) != 0 and unsigned(ipc_entry.ie_object) == unsigned(qport): | |
1285 | ## it's a valid entry, and it points to the port | |
1286 | entry_str = '\t' + GetIPCEntrySummary(ipc_entry, entry_name, rights_type) | |
1287 | ||
1288 | procname = GetProcNameForTask(task) | |
1289 | if unsigned(ipc_port) != 0 and ipc_port.ip_messages.data.port.msgcount > 0: | |
1290 | sys.stderr.write(" checking {:s} busy-port {}:{:#x}...{:30s}\r".format(procname, entry_name, unsigned(ipc_port), '')) | |
1291 | ## Search through busy ports to find descriptors which could | |
1292 | ## contain the only reference to this port! | |
1293 | kmsgp = Cast(ipc_port.ip_messages.data.port.messages.ikmq_base, 'ipc_kmsg_t') | |
1294 | kmsgheadp = kmsgp | |
1295 | while unsigned(kmsgp): | |
1296 | entry_str = FindKmsgPortRefs(entry_str, task, ipc_port, kmsgp, qport) | |
1297 | kmsgp = kmsgp.ikm_next | |
1298 | if kmsgp == kmsgheadp: | |
1299 | break; | |
1300 | if len(entry_str) > 0: | |
1301 | sys.stderr.write("{:80s}\r".format('')) | |
1302 | if port_iteration_do_print_taskname: | |
1303 | print "Task: {0: <#x} {1: <s}".format(task, procname) | |
1304 | print '\t' + GetIPCEntrySummary.header | |
1305 | port_iteration_do_print_taskname = False | |
1306 | print entry_str | |
1307 | ||
1308 | @lldb_command('findportrights', 'R:S:') | |
1309 | def FindPortRights(cmd_args=None, cmd_options={}): | |
1310 | """ Routine to locate and print all extant rights to a given port | |
1311 | Usage: findportrights [-R rights_type] [-S <ipc_space_t>] <ipc_port_t> | |
1312 | -S ipc_space : only search the specified ipc space | |
1313 | -R rights_type : only display rights matching the string 'rights_type' | |
1314 | ||
1315 | types of rights: | |
1316 | 'Dead' : Dead name | |
1317 | 'Set' : Port set | |
1318 | 'S' : Send right | |
1319 | 'R' : Receive right | |
1320 | 'O' : Send-once right | |
1321 | types of notifications: | |
1322 | 'd' : Dead-Name notification requested | |
1323 | 's' : Send-Possible notification armed | |
1324 | 'r' : Send-Possible notification requested | |
1325 | 'n' : No-Senders notification requested | |
1326 | 'x' : Port-destroy notification requested | |
1327 | """ | |
1328 | if not cmd_args: | |
1329 | raise ArgumentError("no port address provided") | |
1330 | port = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_port *') | |
1331 | ||
1332 | rights_type = 0 | |
1333 | if "-R" in cmd_options: | |
1334 | rights_type = cmd_options["-R"] | |
1335 | ||
1336 | tasklist = None | |
1337 | if "-S" in cmd_options: | |
1338 | space = kern.GetValueFromAddress(cmd_options["-S"], 'struct ipc_space *') | |
1339 | tasklist = [ space.is_task ] | |
1340 | ||
1341 | ## Don't include port sets | |
1342 | ## Don't recurse on busy ports (we do that manually) | |
1343 | ## DO log progress | |
1344 | IterateAllPorts(tasklist, FindPortRightsCallback, (port, rights_type), False, False, True) | |
1345 | sys.stderr.write("{:120s}\r".format(' ')) | |
1346 | ||
1347 | print "Done." | |
1348 | return | |
1349 | # EndMacro: findportrights | |
1350 | ||
1351 | # Macro: countallports | |
1352 | ||
1353 | def CountPortsCallback(task, space, ctx, entry_idx, ipc_entry, ipc_port, port_disp): | |
1354 | """ Callback which uses 'ctx' as the set of all ports found in the | |
1355 | iteration. This should *not* be used from a recursive | |
1356 | call to IterateAllPorts. | |
1357 | """ | |
1358 | global intransit_idx | |
1359 | ||
1360 | (p_set, p_intransit, p_bytask) = ctx | |
1361 | ||
1362 | ## Add the port address to the set of all port addresses | |
1363 | p_set.add(unsigned(ipc_port)) | |
1364 | ||
1365 | if entry_idx == intransit_idx: | |
1366 | p_intransit.add(unsigned(ipc_port)) | |
1367 | ||
1368 | if task.active or (task.halting and not task.active): | |
1369 | pname = str(Cast(task.bsd_info, 'proc *').p_name) | |
1370 | if not pname in p_bytask.keys(): | |
1371 | p_bytask[pname] = { 'transit':0, 'table':0, 'other':0 } | |
1372 | if entry_idx == intransit_idx: | |
1373 | p_bytask[pname]['transit'] += 1 | |
1374 | elif entry_idx >= 0: | |
1375 | p_bytask[pname]['table'] += 1 | |
1376 | else: | |
1377 | p_bytask[pname]['other'] += 1 | |
1378 | ||
1379 | ||
1380 | @lldb_command('countallports', 'P') | |
1381 | def CountAllPorts(cmd_args=None, cmd_options={}): | |
1382 | """ Routine to search for all as many references to ipc_port structures in the kernel | |
1383 | that we can find. | |
1384 | Usage: countallports [-P] | |
1385 | -P : include port sets in the count (default: NO) | |
1386 | """ | |
1387 | p_set = set() | |
1388 | p_intransit = set() | |
1389 | p_bytask = {} | |
1390 | ||
1391 | find_psets = False | |
1392 | if "-P" in cmd_options: | |
1393 | find_psets = True | |
1394 | ||
1395 | ## optionally include port sets | |
1396 | ## DO recurse on busy ports | |
1397 | ## DO log progress | |
1398 | IterateAllPorts(None, CountPortsCallback, (p_set, p_intransit, p_bytask), find_psets, True, True) | |
1399 | sys.stderr.write("{:120s}\r".format(' ')) | |
1400 | ||
1401 | print "Total ports found: {:d}".format(len(p_set)) | |
1402 | print "In Transit: {:d}".format(len(p_intransit)) | |
1403 | print "By Task:" | |
1404 | for pname in sorted(p_bytask.keys()): | |
1405 | count = p_bytask[pname] | |
1406 | print "\t{: <20s}: table={: <5d}, transit={: <5d}, other={: <5d}".format(pname, count['table'], count['transit'], count['other']) | |
1407 | return | |
1408 | # EndMacro: countallports | |
1409 | ||
39236c6e A |
1410 | # Macro: showpipestats |
1411 | @lldb_command('showpipestats') | |
1412 | def ShowPipeStats(cmd_args=None): | |
1413 | """ Display pipes usage information in the kernel | |
1414 | """ | |
1415 | print "Number of pipes: {: d}".format(kern.globals.amountpipes) | |
1416 | print "Memory used by pipes: {:s}".format(sizeof_fmt(int(kern.globals.amountpipekva))) | |
1417 | print "Max memory allowed for pipes: {:s}".format(sizeof_fmt(int(kern.globals.maxpipekva))) | |
1418 | # EndMacro: showpipestats | |
1419 | ||
1420 | # Macro: showtaskbusyports | |
1421 | @lldb_command('showtaskbusyports') | |
1422 | def ShowTaskBusyPorts(cmd_args=None): | |
1423 | """ Routine to print information about receive rights belonging to this task that | |
1424 | have enqueued messages. This is oten a sign of a blocked or hung process | |
1425 | Usage: showtaskbusyports <task address> | |
1426 | """ | |
1427 | if not cmd_args: | |
1428 | print "No arguments passed. Please pass in the address of a task" | |
1429 | print ShowTaskBusyPorts.__doc__ | |
1430 | return | |
1431 | task = kern.GetValueFromAddress(cmd_args[0], 'task_t') | |
3e170ce0 | 1432 | PrintTaskBusyPorts(task) |
39236c6e A |
1433 | return |
1434 | ||
3e170ce0 | 1435 | def PrintTaskBusyPorts(task): |
39236c6e A |
1436 | """ Prints all busy ports for a given task. ie. all receive rights belonging |
1437 | to this task that have enqueued messages. | |
1438 | params: | |
1439 | task : core.value representing a task in kernel | |
1440 | returns: | |
1441 | str : String containing information about the given task's busy ports | |
1442 | """ | |
1443 | isp = task.itk_space | |
1444 | i = 0 | |
39236c6e A |
1445 | while i < isp.is_table_size: |
1446 | iep = addressof(isp.is_table[i]) | |
1447 | if iep.ie_bits & 0x00020000: | |
1448 | port = Cast(iep.ie_object, 'ipc_port_t') | |
1449 | if port.ip_messages.data.port.msgcount > 0: | |
3e170ce0 A |
1450 | print PrintPortSummary.header |
1451 | PrintPortSummary(port) | |
39236c6e | 1452 | i = i + 1 |
3e170ce0 | 1453 | return |
39236c6e A |
1454 | # EndMacro: showtaskbusyports |
1455 | ||
1456 | # Macro: showallbusyports | |
1457 | @lldb_command('showallbusyports') | |
1458 | def ShowAllBusyPorts(cmd_args=None): | |
1459 | """ Routine to print information about all receive rights on the system that | |
1460 | have enqueued messages. | |
1461 | """ | |
1462 | task_queue_head = kern.globals.tasks | |
3e170ce0 | 1463 | |
39236c6e | 1464 | for tsk in kern.tasks: |
3e170ce0 | 1465 | PrintTaskBusyPorts(tsk) |
39236c6e A |
1466 | return |
1467 | # EndMacro: showallbusyports | |
1468 | ||
d9a64523 A |
1469 | # Macro: showbusyportsummary |
1470 | @lldb_command('showbusyportsummary') | |
1471 | def ShowBusyPortSummary(cmd_args=None): | |
1472 | """ Routine to print a summary of information about all receive rights | |
1473 | on the system that have enqueued messages. | |
1474 | """ | |
1475 | task_queue_head = kern.globals.tasks | |
1476 | ||
1477 | ipc_table_size = 0 | |
1478 | ipc_busy_ports = 0 | |
1479 | ipc_msgs = 0 | |
1480 | ||
1481 | print GetTaskBusyIPCSummary.header | |
1482 | for tsk in kern.tasks: | |
1483 | (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tsk) | |
1484 | ipc_table_size += table_size | |
1485 | ipc_busy_ports += nbusy | |
1486 | ipc_msgs += nmsgs | |
1487 | print summary | |
1488 | for t in kern.terminated_tasks: | |
1489 | (summary, table_size, nbusy, nmsgs) = GetTaskBusyIPCSummary(tsk) | |
1490 | ipc_table_size += table_size | |
1491 | ipc_busy_ports += nbusy | |
1492 | ipc_msgs += nmsgs | |
1493 | print summary | |
1494 | print "Total Table Size: {:d}, Busy Ports: {:d}, Messages in-flight: {:d}".format(ipc_table_size, ipc_busy_ports, ipc_msgs) | |
1495 | return | |
1496 | # EndMacro: showbusyportsummary | |
1497 | ||
3e170ce0 A |
1498 | # Macro: showport: |
1499 | @lldb_command('showport','K') | |
1500 | def ShowPort(cmd_args=None, cmd_options={}): | |
1501 | """ Routine that lists details about a given IPC port | |
1502 | Syntax: (lldb) showport 0xaddr | |
1503 | """ | |
1504 | show_kmsgs = True | |
1505 | if "-K" in cmd_options: | |
1506 | show_kmsgs = False | |
1507 | if not cmd_args: | |
1508 | print "Please specify the address of the port whose details you want to print" | |
1509 | print ShowPort.__doc__ | |
1510 | return | |
1511 | port = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_port *') | |
1512 | print PrintPortSummary.header | |
1513 | PrintPortSummary(port, show_kmsgs) | |
1514 | # EndMacro: showport | |
1515 | ||
39236c6e | 1516 | # Macro: showmqueue: |
3e170ce0 A |
1517 | @lldb_command('showmqueue', "S:") |
1518 | def ShowMQueue(cmd_args=None, cmd_options={}): | |
39236c6e | 1519 | """ Routine that lists details about a given mqueue |
3e170ce0 | 1520 | Syntax: (lldb) showmqueue 0xaddr [-S ipc_space] |
39236c6e A |
1521 | """ |
1522 | if not cmd_args: | |
1523 | print "Please specify the address of the ipc_mqueue whose details you want to print" | |
1524 | print ShowMQueue.__doc__ | |
1525 | return | |
3e170ce0 A |
1526 | space = 0 |
1527 | if "-S" in cmd_options: | |
1528 | space = kern.GetValueFromAddress(cmd_options["-S"], 'struct ipc_space *') | |
39236c6e | 1529 | mqueue = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_mqueue *') |
3e170ce0 | 1530 | wq_type = mqueue.data.pset.setq.wqset_q.waitq_type |
39236c6e | 1531 | if int(wq_type) == 3: |
fe8ab488 | 1532 | psetoff = getfieldoffset('struct ipc_pset', 'ips_messages') |
39236c6e | 1533 | pset = unsigned(ArgumentStringToInt(cmd_args[0])) - unsigned(psetoff) |
3e170ce0 A |
1534 | print PrintPortSetSummary.header |
1535 | PrintPortSetSummary(kern.GetValueFromAddress(pset, 'struct ipc_pset *'), space) | |
1536 | elif int(wq_type) == 2: | |
39236c6e A |
1537 | portoff = getfieldoffset('struct ipc_port', 'ip_messages') |
1538 | port = unsigned(ArgumentStringToInt(cmd_args[0])) - unsigned(portoff) | |
3e170ce0 A |
1539 | print PrintPortSummary.header |
1540 | PrintPortSummary(kern.GetValueFromAddress(port, 'struct ipc_port *')) | |
1541 | else: | |
1542 | print "Invalid mqueue? (waitq type {:d} is invalid)".format(int(wq_type)) | |
39236c6e A |
1543 | # EndMacro: showmqueue |
1544 | ||
fe8ab488 A |
1545 | # Macro: showkmsg: |
1546 | @lldb_command('showkmsg') | |
1547 | def ShowKMSG(cmd_args=[]): | |
1548 | """ Show detail information about a <ipc_kmsg_t> structure | |
1549 | Usage: (lldb) showkmsg <ipc_kmsg_t> | |
1550 | """ | |
1551 | if not cmd_args: | |
1552 | raise ArgumentError('Invalid arguments') | |
1553 | kmsg = kern.GetValueFromAddress(cmd_args[0], 'ipc_kmsg_t') | |
1554 | print GetKMsgSummary.header | |
1555 | print GetKMsgSummary(kmsg) | |
1556 | ||
1557 | # EndMacro: showkmsg | |
1558 | ||
39236c6e | 1559 | # Macro: showpset |
3e170ce0 A |
1560 | @lldb_command('showpset', "S:") |
1561 | def ShowPSet(cmd_args=None, cmd_options={}): | |
39236c6e | 1562 | """ Routine that prints details for a given ipc_pset * |
3e170ce0 | 1563 | Syntax: (lldb) showpset 0xaddr [-S ipc_space] |
39236c6e A |
1564 | """ |
1565 | if not cmd_args: | |
1566 | print "Please specify the address of the pset whose details you want to print" | |
1567 | print ShowPSet.__doc__ | |
1568 | return | |
3e170ce0 A |
1569 | space = 0 |
1570 | if "-S" in cmd_options: | |
1571 | space = kern.GetValueFromAddress(cmd_options["-S"], 'struct ipc_space *') | |
1572 | ||
1573 | print PrintPortSetSummary.header | |
1574 | PrintPortSetSummary(kern.GetValueFromAddress(cmd_args[0], 'ipc_pset *'), space) | |
39236c6e A |
1575 | # EndMacro: showpset |
1576 | ||
fe8ab488 A |
1577 | # IPC importance inheritance related macros. |
1578 | ||
1579 | @lldb_command('showalliits') | |
1580 | def ShowAllIITs(cmd_args=[], cmd_options={}): | |
1581 | """ Development only macro. Show list of all iits allocated in the system. """ | |
1582 | try: | |
1583 | iit_queue = kern.globals.global_iit_alloc_queue | |
1584 | except ValueError: | |
1585 | print "This debug macro is only available in development or debug kernels" | |
1586 | return | |
1587 | ||
1588 | print GetIPCImportantTaskSummary.header | |
1589 | for iit in IterateQueue(iit_queue, 'struct ipc_importance_task *', 'iit_allocation'): | |
1590 | print GetIPCImportantTaskSummary(iit) | |
1591 | return | |
1592 | ||
1593 | @header("{: <18s} {: <3s} {: <18s} {: <20s} {: <18s} {: <8s}".format("ipc_imp_inherit", "don", "to_task", "proc_name", "from_elem", "depth")) | |
1594 | @lldb_type_summary(['ipc_importance_inherit *', 'ipc_importance_inherit_t']) | |
1595 | def GetIPCImportanceInheritSummary(iii): | |
1596 | """ describes iii object of type ipc_importance_inherit_t * """ | |
1597 | out_str = "" | |
1598 | fmt = "{o: <#018x} {don: <3s} {o.iii_to_task.iit_task: <#018x} {task_name: <20s} {o.iii_from_elem: <#018x} {o.iii_depth: <#08x}" | |
1599 | donating_str = "" | |
1600 | if unsigned(iii.iii_donating): | |
1601 | donating_str = "DON" | |
1602 | taskname = GetProcNameForTask(iii.iii_to_task.iit_task) | |
1603 | if hasattr(iii.iii_to_task, 'iit_bsd_pid'): | |
1604 | taskname = "({:d}) {:s}".format(iii.iii_to_task.iit_bsd_pid, iii.iii_to_task.iit_procname) | |
1605 | out_str += fmt.format(o=iii, task_name = taskname, don=donating_str) | |
1606 | return out_str | |
1607 | ||
1608 | @static_var('recursion_count', 0) | |
1609 | @header("{: <18s} {: <4s} {: <8s} {: <8s} {: <18s} {: <18s}".format("iie", "type", "refs", "made", "#kmsgs", "#inherits")) | |
1610 | @lldb_type_summary(['ipc_importance_elem *']) | |
1611 | def GetIPCImportanceElemSummary(iie): | |
1612 | """ describes an ipc_importance_elem * object """ | |
1613 | ||
1614 | if GetIPCImportanceElemSummary.recursion_count > 500: | |
1615 | GetIPCImportanceElemSummary.recursion_count = 0 | |
1616 | return "Recursion of 500 reached" | |
1617 | ||
1618 | out_str = '' | |
1619 | fmt = "{: <#018x} {: <4s} {: <8d} {: <8d} {: <#018x} {: <#018x}" | |
fe8ab488 A |
1620 | if unsigned(iie.iie_bits) & 0x80000000: |
1621 | type_str = "INH" | |
39037602 A |
1622 | inherit_count = 0 |
1623 | else: | |
1624 | type_str = 'TASK' | |
1625 | iit = Cast(iie, 'struct ipc_importance_task *') | |
1626 | inherit_count = sum(1 for i in IterateQueue(iit.iit_inherits, 'struct ipc_importance_inherit *', 'iii_inheritance')) | |
1627 | ||
fe8ab488 A |
1628 | refs = unsigned(iie.iie_bits) & 0x7fffffff |
1629 | made_refs = unsigned(iie.iie_made) | |
1630 | kmsg_count = sum(1 for i in IterateQueue(iie.iie_kmsgs, 'struct ipc_kmsg *', 'ikm_inheritance')) | |
fe8ab488 A |
1631 | out_str += fmt.format(iie, type_str, refs, made_refs, kmsg_count, inherit_count) |
1632 | if config['verbosity'] > vHUMAN: | |
1633 | if kmsg_count > 0: | |
1634 | out_str += "\n\t"+ GetKMsgSummary.header | |
1635 | for k in IterateQueue(iie.iie_kmsgs, 'struct ipc_kmsg *', 'ikm_inheritance'): | |
3e170ce0 | 1636 | out_str += "\t" + "{: <#018x}".format(k.ikm_header.msgh_remote_port) + ' ' + GetKMsgSummary(k, "\t").lstrip() |
fe8ab488 A |
1637 | out_str += "\n" |
1638 | if inherit_count > 0: | |
1639 | out_str += "\n\t" + GetIPCImportanceInheritSummary.header + "\n" | |
39037602 | 1640 | for i in IterateQueue(iit.iit_inherits, 'struct ipc_importance_inherit *', 'iii_inheritance'): |
fe8ab488 A |
1641 | out_str += "\t" + GetIPCImportanceInheritSummary(i) + "\n" |
1642 | out_str += "\n" | |
1643 | if type_str == "INH": | |
1644 | iii = Cast(iie, 'struct ipc_importance_inherit *') | |
1645 | out_str += "Inherit from: " + GetIPCImportanceElemSummary(iii.iii_from_elem) | |
1646 | ||
1647 | return out_str | |
1648 | ||
1649 | @header("{: <18s} {: <18s} {: <20s}".format("iit", "task", "name")) | |
1650 | @lldb_type_summary(['ipc_importance_task *']) | |
1651 | def GetIPCImportantTaskSummary(iit): | |
1652 | """ iit is a ipc_importance_task value object. | |
1653 | """ | |
1654 | fmt = "{: <#018x} {: <#018x} {: <20s}" | |
1655 | out_str='' | |
1656 | pname = GetProcNameForTask(iit.iit_task) | |
1657 | if hasattr(iit, 'iit_bsd_pid'): | |
1658 | pname = "({:d}) {:s}".format(iit.iit_bsd_pid, iit.iit_procname) | |
1659 | out_str += fmt.format(iit, iit.iit_task, pname) | |
1660 | return out_str | |
1661 | ||
1662 | @lldb_command('showallimportancetasks') | |
1663 | def ShowIPCImportanceTasks(cmd_args=[], cmd_options={}): | |
1664 | """ display a list of all tasks with ipc importance information. | |
1665 | Usage: (lldb) showallimportancetasks | |
1666 | Tip: add "-v" to see detailed information on each kmsg or inherit elems | |
1667 | """ | |
1668 | print ' ' + GetIPCImportantTaskSummary.header + ' ' + GetIPCImportanceElemSummary.header | |
1669 | for t in kern.tasks: | |
1670 | s = "" | |
1671 | if unsigned(t.task_imp_base): | |
1672 | s += ' ' + GetIPCImportantTaskSummary(t.task_imp_base) | |
1673 | s += ' ' + GetIPCImportanceElemSummary(addressof(t.task_imp_base.iit_elem)) | |
1674 | print s | |
1675 | ||
1676 | @lldb_command('showipcimportance', '') | |
1677 | def ShowIPCImportance(cmd_args=[], cmd_options={}): | |
1678 | """ Describe an importance from <ipc_importance_elem_t> argument. | |
1679 | Usage: (lldb) showimportance <ipc_importance_elem_t> | |
1680 | """ | |
1681 | if not cmd_args: | |
1682 | raise ArgumentError("Please provide valid argument") | |
1683 | ||
1684 | elem = kern.GetValueFromAddress(cmd_args[0], 'ipc_importance_elem_t') | |
1685 | print GetIPCImportanceElemSummary.header | |
1686 | print GetIPCImportanceElemSummary(elem) | |
1687 | ||
1688 | @header("{: <18s} {: <10s} {: <18s} {: <18s} {: <8s} {: <5s} {: <5s} {: <5s}".format("ivac", "refs", "port", "tbl", "tblsize", "index", "Grow", "freelist")) | |
1689 | @lldb_type_summary(['ipc_voucher_attr_control *', 'ipc_voucher_attr_control_t']) | |
1690 | def GetIPCVoucherAttrControlSummary(ivac): | |
1691 | """ describes a voucher attribute control settings """ | |
1692 | out_str = "" | |
1693 | fmt = "{c: <#018x} {c.ivac_refs: <10d} {c.ivac_port: <#018x} {c.ivac_table: <#018x} {c.ivac_table_size: <8d} {c.ivac_key_index: <5d} {growing: <5s} {c.ivac_freelist: <5d}" | |
1694 | growing_str = "" | |
1695 | ||
1696 | if unsigned(ivac) == 0: | |
1697 | return "{: <#018x}".format(ivac) | |
1698 | ||
1699 | if unsigned(ivac.ivac_is_growing): | |
1700 | growing_str = "Y" | |
1701 | out_str += fmt.format(c=ivac, growing = growing_str) | |
1702 | return out_str | |
1703 | ||
1704 | @lldb_command('showivac','') | |
1705 | def ShowIPCVoucherAttributeControl(cmd_args=[], cmd_options={}): | |
1706 | """ Show summary of voucher attribute contols. | |
1707 | Usage: (lldb) showivac <ipc_voucher_attr_control_t> | |
1708 | """ | |
1709 | if not cmd_args: | |
1710 | raise ArgumentError("Please provide correct arguments.") | |
1711 | ivac = kern.GetValueFromAddress(cmd_args[0], 'ipc_voucher_attr_control_t') | |
1712 | print GetIPCVoucherAttrControlSummary.header | |
1713 | print GetIPCVoucherAttrControlSummary(ivac) | |
1714 | if config['verbosity'] > vHUMAN: | |
1715 | cur_entry_index = 0 | |
1716 | last_entry_index = unsigned(ivac.ivac_table_size) | |
1717 | print "index " + GetIPCVoucherAttributeEntrySummary.header | |
1718 | while cur_entry_index < last_entry_index: | |
1719 | print "{: <5d} ".format(cur_entry_index) + GetIPCVoucherAttributeEntrySummary(addressof(ivac.ivac_table[cur_entry_index])) | |
1720 | cur_entry_index += 1 | |
1721 | ||
1722 | ||
1723 | ||
1724 | ||
1725 | @header("{: <18s} {: <30s} {: <30s} {: <30s} {: <30s} {: <30s}".format("ivam", "get_value_fn", "extract_fn", "release_value_fn", "command_fn", "release_fn")) | |
1726 | @lldb_type_summary(['ipc_voucher_attr_manager *', 'ipc_voucher_attr_manager_t']) | |
1727 | def GetIPCVoucherAttrManagerSummary(ivam): | |
1728 | """ describes a voucher attribute manager settings """ | |
1729 | out_str = "" | |
1730 | fmt = "{: <#018x} {: <30s} {: <30s} {: <30s} {: <30s} {: <30s}" | |
1731 | ||
1732 | if unsigned(ivam) == 0 : | |
1733 | return "{: <#018x}".format(ivam) | |
1734 | ||
1735 | get_value_fn = kern.Symbolicate(unsigned(ivam.ivam_get_value)) | |
1736 | extract_fn = kern.Symbolicate(unsigned(ivam.ivam_extract_content)) | |
1737 | release_value_fn = kern.Symbolicate(unsigned(ivam.ivam_release_value)) | |
1738 | command_fn = kern.Symbolicate(unsigned(ivam.ivam_command)) | |
1739 | release_fn = kern.Symbolicate(unsigned(ivam.ivam_release)) | |
1740 | out_str += fmt.format(ivam, get_value_fn, extract_fn, release_value_fn, command_fn, release_fn) | |
1741 | return out_str | |
1742 | ||
1743 | ||
1744 | ||
1745 | @header("{: <18s} {: <10s} {:s} {:s}".format("ivgte", "key", GetIPCVoucherAttrControlSummary.header.strip(), GetIPCVoucherAttrManagerSummary.header.strip())) | |
1746 | @lldb_type_summary(['ipc_voucher_global_table_element *', 'ipc_voucher_global_table_element_t']) | |
1747 | def GetIPCVoucherGlobalTableElementSummary(ivgte): | |
1748 | """ describes a ipc_voucher_global_table_element object """ | |
1749 | out_str = "" | |
1750 | fmt = "{g: <#018x} {g.ivgte_key: <10d} {ctrl_s:s} {mgr_s:s}" | |
1751 | out_str += fmt.format(g=ivgte, ctrl_s=GetIPCVoucherAttrControlSummary(ivgte.ivgte_control), mgr_s=GetIPCVoucherAttrManagerSummary(ivgte.ivgte_manager)) | |
1752 | return out_str | |
1753 | ||
1754 | @lldb_command('showglobalvouchertable', '') | |
1755 | def ShowGlobalVoucherTable(cmd_args=[], cmd_options={}): | |
1756 | """ show detailed information of all voucher attribute managers registered with vouchers system | |
1757 | Usage: (lldb) showglobalvouchertable | |
1758 | """ | |
1759 | entry_size = sizeof(kern.globals.iv_global_table[0]) | |
1760 | elems = sizeof(kern.globals.iv_global_table) / entry_size | |
1761 | print GetIPCVoucherGlobalTableElementSummary.header | |
1762 | for i in range(elems): | |
1763 | elt = addressof(kern.globals.iv_global_table[i]) | |
1764 | print GetIPCVoucherGlobalTableElementSummary(elt) | |
1765 | ||
1766 | # Type summaries for Bag of Bits. | |
1767 | ||
1768 | @lldb_type_summary(['user_data_value_element', 'user_data_element_t']) | |
1769 | @header("{0: <20s} {1: <16s} {2: <20s} {3: <20s} {4: <16s} {5: <20s}".format("user_data_ve", "maderefs", "checksum", "hash value", "size", "data")) | |
1770 | def GetBagofBitsElementSummary(data_element): | |
1771 | """ Summarizes the Bag of Bits element | |
1772 | params: data_element = value of the object of type user_data_value_element_t | |
1773 | returns: String with summary of the type. | |
1774 | """ | |
1775 | format_str = "{0: <#020x} {1: <16d} {2: <#020x} {3: <#020x} {4: <16d}" | |
1776 | out_string = format_str.format(data_element, unsigned(data_element.e_made), data_element.e_sum, data_element.e_hash, unsigned(data_element.e_size)) | |
1777 | out_string += " 0x" | |
1778 | ||
1779 | for i in range(0, (unsigned(data_element.e_size) - 1)): | |
1780 | out_string += "{:02x}".format(int(data_element.e_data[i])) | |
1781 | return out_string | |
1782 | ||
1783 | def GetIPCHandleSummary(handle_ptr): | |
1784 | """ converts a handle value inside a voucher attribute table to ipc element and returns appropriate summary. | |
1785 | params: handle_ptr - uint64 number stored in handle of voucher. | |
1786 | returns: str - string summary of the element held in internal structure | |
1787 | """ | |
1788 | elem = kern.GetValueFromAddress(handle_ptr, 'ipc_importance_elem_t') | |
1789 | if elem.iie_bits & 0x80000000 : | |
1790 | iie = Cast(elem, 'struct ipc_importance_inherit *') | |
1791 | return GetIPCImportanceInheritSummary(iie) | |
1792 | else: | |
1793 | iit = Cast(elem, 'struct ipc_importance_task *') | |
1794 | return GetIPCImportantTaskSummary(iit) | |
1795 | ||
1796 | def GetATMHandleSummary(handle_ptr): | |
1797 | """ Convert a handle value to atm value and returns corresponding summary of its fields. | |
1798 | params: handle_ptr - uint64 number stored in handle of voucher | |
1799 | returns: str - summary of atm value | |
1800 | """ | |
1801 | elem = kern.GetValueFromAddress(handle_ptr, 'atm_value *') | |
1802 | return GetATMValueSummary(elem) | |
1803 | ||
1804 | def GetBankHandleSummary(handle_ptr): | |
1805 | """ converts a handle value inside a voucher attribute table to bank element and returns appropriate summary. | |
1806 | params: handle_ptr - uint64 number stored in handle of voucher. | |
1807 | returns: str - summary of bank element | |
1808 | """ | |
490019cf A |
1809 | if handle_ptr == 1 : |
1810 | return "Bank task of Current task" | |
fe8ab488 A |
1811 | elem = kern.GetValueFromAddress(handle_ptr, 'bank_element_t') |
1812 | if elem.be_type & 1 : | |
1813 | ba = Cast(elem, 'struct bank_account *') | |
1814 | return GetBankAccountSummary(ba) | |
1815 | else: | |
1816 | bt = Cast(elem, 'struct bank_task *') | |
1817 | return GetBankTaskSummary(bt) | |
1818 | ||
1819 | def GetBagofBitsHandleSummary(handle_ptr): | |
1820 | """ Convert a handle value to bag of bits value and returns corresponding summary of its fields. | |
1821 | params: handle_ptr - uint64 number stored in handle of voucher | |
1822 | returns: str - summary of bag of bits element | |
1823 | """ | |
1824 | elem = kern.GetValueFromAddress(handle_ptr, 'user_data_element_t') | |
1825 | return GetBagofBitsElementSummary(elem) | |
1826 | ||
1827 | @static_var('attr_managers',{1: GetATMHandleSummary, 2: GetIPCHandleSummary, 3: GetBankHandleSummary, 7: GetBagofBitsHandleSummary}) | |
1828 | def GetHandleSummaryForKey(handle_ptr, key_num): | |
1829 | """ Get a summary of handle pointer from the voucher attribute manager. | |
1830 | For example key 1 -> ATM and it puts atm_value_t in the handle. So summary of it would be atm value and refs etc. | |
1831 | key 2 -> ipc and it puts either ipc_importance_inherit_t or ipc_important_task_t. | |
1832 | key 3 -> Bank and it puts either bank_task_t or bank_account_t. | |
1833 | key 7 -> Bag of Bits and it puts user_data_element_t in handle. So summary of it would be Bag of Bits content and refs etc. | |
1834 | """ | |
1835 | key_num = int(key_num) | |
1836 | if key_num not in GetHandleSummaryForKey.attr_managers: | |
1837 | return "Unknown key %d" % key_num | |
1838 | return GetHandleSummaryForKey.attr_managers[key_num](handle_ptr) | |
1839 | ||
1840 | ||
1841 | @header("{: <18s} {: <18s} {: <10s} {: <4s} {: <18s} {: <18s}".format("ivace", "value_handle", "#refs", "rel?", "maderefs", "next_layer")) | |
1842 | @lldb_type_summary(['ivac_entry *', 'ivac_entry_t']) | |
1843 | def GetIPCVoucherAttributeEntrySummary(ivace, manager_key_num = 0): | |
1844 | """ Get summary for voucher attribute entry. | |
1845 | """ | |
1846 | out_str = "" | |
1847 | fmt = "{e: <#018x} {e.ivace_value: <#018x} {e.ivace_refs: <10d} {release: <4s} {made_refs: <18s} {next_layer: <18s}" | |
1848 | release_str = "" | |
1849 | free_str = "" | |
1850 | made_refs = "" | |
1851 | next_layer = "" | |
1852 | ||
1853 | if unsigned(ivace.ivace_releasing): | |
1854 | release_str = "Y" | |
1855 | if unsigned(ivace.ivace_free): | |
1856 | free_str = 'F' | |
1857 | if unsigned(ivace.ivace_layered): | |
1858 | next_layer = "{: <#018x}".format(ivace.ivace_u.ivaceu_layer) | |
1859 | else: | |
1860 | made_refs = "{: <18d}".format(ivace.ivace_u.ivaceu_made) | |
1861 | ||
1862 | out_str += fmt.format(e=ivace, release=release_str, made_refs=made_refs, next_layer=next_layer) | |
1863 | if config['verbosity'] > vHUMAN and manager_key_num > 0: | |
1864 | out_str += " " + GetHandleSummaryForKey(unsigned(ivace.ivace_value), manager_key_num) | |
1865 | if config['verbosity'] > vHUMAN : | |
1866 | out_str += ' {: <2s} {: <4d} {: <4d}'.format(free_str, ivace.ivace_next, ivace.ivace_index) | |
1867 | return out_str | |
1868 | ||
1869 | @lldb_command('showivacfreelist','') | |
1870 | def ShowIVACFreeList(cmd_args=[], cmd_options={}): | |
1871 | """ Walk the free list and print every entry in the list. | |
1872 | usage: (lldb) showivacfreelist <ipc_voucher_attr_control_t> | |
1873 | """ | |
1874 | if not cmd_args: | |
1875 | raise ArgumentError('Please provide <ipc_voucher_attr_control_t>') | |
1876 | ivac = kern.GetValueFromAddress(cmd_args[0], 'ipc_voucher_attr_control_t') | |
1877 | print GetIPCVoucherAttrControlSummary.header | |
1878 | print GetIPCVoucherAttrControlSummary(ivac) | |
1879 | if unsigned(ivac.ivac_freelist) == 0: | |
1880 | print "ivac table is full" | |
1881 | return | |
1882 | print "index " + GetIPCVoucherAttributeEntrySummary.header | |
1883 | next_free = unsigned(ivac.ivac_freelist) | |
1884 | while next_free != 0: | |
1885 | print "{: <5d} ".format(next_free) + GetIPCVoucherAttributeEntrySummary(addressof(ivac.ivac_table[next_free])) | |
1886 | next_free = unsigned(ivac.ivac_table[next_free].ivace_next) | |
1887 | ||
1888 | ||
1889 | ||
1890 | @header('{: <18s} {: <8s} {: <18s} {: <18s} {: <18s} {: <18s} {: <18s}'.format("ipc_voucher", "refs", "checksum", "hash", "tbl_size", "table", "voucher_port")) | |
1891 | @lldb_type_summary(['ipc_voucher *', 'ipc_voucher_t']) | |
1892 | def GetIPCVoucherSummary(voucher, show_entries=False): | |
1893 | """ describe a voucher from its ipc_voucher * object """ | |
1894 | out_str = "" | |
1895 | fmt = "{v: <#018x} {v.iv_refs: <8d} {v.iv_sum: <#018x} {v.iv_hash: <#018x} {v.iv_table_size: <#018x} {v.iv_table: <#018x} {v.iv_port: <#018x}" | |
1896 | out_str += fmt.format(v = voucher) | |
1897 | entries_str = '' | |
1898 | if show_entries or config['verbosity'] > vHUMAN: | |
1899 | elems = unsigned(voucher.iv_table_size) | |
1900 | entries_header_str = "\n\t" + "{: <5s} {: <3s} {: <16s} {: <30s}".format("index", "key", "value_index", "manager") + " " + GetIPCVoucherAttributeEntrySummary.header | |
1901 | fmt = "{: <5d} {: <3d} {: <16d} {: <30s}" | |
1902 | for i in range(elems): | |
1903 | voucher_entry_index = unsigned(voucher.iv_inline_table[i]) | |
1904 | if voucher_entry_index: | |
1905 | s = fmt.format(i, GetVoucherManagerKeyForIndex(i), voucher_entry_index, GetVoucherAttributeManagerNameForIndex(i)) | |
1906 | e = GetVoucherValueHandleFromVoucherForIndex(voucher, i) | |
1907 | if e is not None: | |
1908 | s += " " + GetIPCVoucherAttributeEntrySummary(addressof(e), GetVoucherManagerKeyForIndex(i) ) | |
1909 | if entries_header_str : | |
1910 | entries_str = entries_header_str | |
1911 | entries_header_str = '' | |
1912 | entries_str += "\n\t" + s | |
1913 | if not entries_header_str: | |
1914 | entries_str += "\n\t" | |
1915 | out_str += entries_str | |
1916 | return out_str | |
1917 | ||
1918 | def GetVoucherManagerKeyForIndex(idx): | |
1919 | """ Returns key number for index based on global table. Will raise index error if value is incorrect | |
1920 | """ | |
1921 | return unsigned(kern.globals.iv_global_table[idx].ivgte_key) | |
1922 | ||
1923 | def GetVoucherAttributeManagerForKey(k): | |
1924 | """ Walks through the iv_global_table and finds the attribute manager name | |
1925 | params: k - int key number of the manager | |
1926 | return: cvalue - the attribute manager object. | |
1927 | None - if not found | |
1928 | """ | |
1929 | retval = None | |
1930 | entry_size = sizeof(kern.globals.iv_global_table[0]) | |
1931 | elems = sizeof(kern.globals.iv_global_table) / entry_size | |
1932 | for i in range(elems): | |
1933 | elt = addressof(kern.globals.iv_global_table[i]) | |
1934 | if k == unsigned(elt.ivgte_key): | |
1935 | retval = elt.ivgte_manager | |
1936 | break | |
1937 | return retval | |
1938 | ||
1939 | def GetVoucherAttributeControllerForKey(k): | |
1940 | """ Walks through the iv_global_table and finds the attribute controller | |
1941 | params: k - int key number of the manager | |
1942 | return: cvalue - the attribute controller object. | |
1943 | None - if not found | |
1944 | """ | |
1945 | retval = None | |
1946 | entry_size = sizeof(kern.globals.iv_global_table[0]) | |
1947 | elems = sizeof(kern.globals.iv_global_table) / entry_size | |
1948 | for i in range(elems): | |
1949 | elt = addressof(kern.globals.iv_global_table[i]) | |
1950 | if k == unsigned(elt.ivgte_key): | |
1951 | retval = elt.ivgte_control | |
1952 | break | |
1953 | return retval | |
1954 | ||
1955 | ||
1956 | def GetVoucherAttributeManagerName(ivam): | |
1957 | """ find the name of the ivam object | |
1958 | param: ivam - cvalue object of type ipc_voucher_attr_manager_t | |
1959 | returns: str - name of the manager | |
1960 | """ | |
1961 | return kern.Symbolicate(unsigned(ivam)) | |
1962 | ||
1963 | def GetVoucherAttributeManagerNameForIndex(idx): | |
1964 | """ get voucher attribute manager name for index | |
1965 | return: str - name of the attribute manager object | |
1966 | """ | |
1967 | return GetVoucherAttributeManagerName(GetVoucherAttributeManagerForKey(GetVoucherManagerKeyForIndex(idx))) | |
1968 | ||
1969 | def GetVoucherValueHandleFromVoucherForIndex(voucher, idx): | |
1970 | """ traverse the voucher attrs and get value_handle in the voucher attr controls table | |
1971 | params: | |
1972 | voucher - cvalue object of type ipc_voucher_t | |
1973 | idx - int index in the entries for which you wish to get actual handle for | |
1974 | returns: cvalue object of type ivac_entry_t | |
1975 | None if no handle found. | |
1976 | """ | |
1977 | manager_key = GetVoucherManagerKeyForIndex(idx) | |
1978 | voucher_num_elems = unsigned(voucher.iv_table_size) | |
1979 | if idx >= voucher_num_elems: | |
1980 | debuglog("idx %d is out of range max: %d" % (idx, voucher_num_elems)) | |
1981 | return None | |
1982 | voucher_entry_value = unsigned(voucher.iv_inline_table[idx]) | |
1983 | debuglog("manager_key %d" % manager_key) | |
1984 | ivac = GetVoucherAttributeControllerForKey(manager_key) | |
1985 | if ivac is None or unsigned(ivac) == 0: | |
1986 | debuglog("No voucher attribute controller for idx %d" % idx) | |
1987 | return None | |
1988 | ||
1989 | ivac = kern.GetValueFromAddress(unsigned(ivac), 'ipc_voucher_attr_control_t') # ??? No idea why lldb does not addressof directly | |
1990 | ivace_table = ivac.ivac_table | |
1991 | if voucher_entry_value >= unsigned(ivac.ivac_table_size): | |
1992 | print "Failed to get ivace for value %d in table of size %d" % (voucher_entry_value, unsigned(ivac.ivac_table_size)) | |
1993 | return None | |
1994 | return ivace_table[voucher_entry_value] | |
1995 | ||
1996 | ||
1997 | ||
1998 | @lldb_command('showallvouchers') | |
1999 | def ShowAllVouchers(cmd_args=[], cmd_options={}): | |
2000 | """ Display a list of all vouchers in the global voucher hash table | |
2001 | Usage: (lldb) showallvouchers | |
2002 | """ | |
2003 | iv_hash_table = kern.globals.ivht_bucket | |
2004 | num_buckets = sizeof(kern.globals.ivht_bucket) / sizeof(kern.globals.ivht_bucket[0]) | |
2005 | print GetIPCVoucherSummary.header | |
2006 | for i in range(num_buckets): | |
2007 | for v in IterateQueue(iv_hash_table[i], 'ipc_voucher_t', 'iv_hash_link'): | |
2008 | print GetIPCVoucherSummary(v) | |
2009 | ||
2010 | @lldb_command('showvoucher', '') | |
2011 | def ShowVoucher(cmd_args=[], cmd_options={}): | |
2012 | """ Describe a voucher from <ipc_voucher_t> argument. | |
2013 | Usage: (lldb) showvoucher <ipc_voucher_t> | |
2014 | """ | |
2015 | if not cmd_args: | |
2016 | raise ArgumentError("Please provide valid argument") | |
2017 | ||
2018 | voucher = kern.GetValueFromAddress(cmd_args[0], 'ipc_voucher_t') | |
2019 | print GetIPCVoucherSummary.header | |
2020 | print GetIPCVoucherSummary(voucher, show_entries=True) | |
fe8ab488 | 2021 | |
39037602 A |
2022 | def GetSpaceSendRightEntries(space, port): |
2023 | """ Get entry summaries for all send rights to port address in an IPC space. | |
2024 | params: | |
2025 | space - the IPC space to search for send rights | |
2026 | port_addr - the port address to match, or 0 to get all send rights | |
2027 | returns: an array of IPC entries | |
2028 | """ | |
2029 | entry_table = space.is_table | |
2030 | ports = int(space.is_table_size) | |
2031 | i = 0 | |
2032 | entries = [] | |
2033 | ||
2034 | while i < ports: | |
2035 | entry = GetObjectAtIndexFromArray(entry_table, i) | |
2036 | ||
2037 | entry_ie_bits = unsigned(entry.ie_bits) | |
2038 | if (entry_ie_bits & 0x00010000) != 0 and (not port or entry.ie_object == port): | |
2039 | entries.append(entry) | |
2040 | i += 1 | |
2041 | ||
2042 | return entries | |
2043 | ||
2044 | @lldb_command('showportsendrights') | |
2045 | def ShowPortSendRights(cmd_args=[], cmd_options={}): | |
2046 | """ Display a list of send rights across all tasks for a given port. | |
2047 | Usage: (lldb) showportsendrights <ipc_port_t> | |
2048 | """ | |
2049 | if not cmd_args: | |
2050 | raise ArgumentError("no port address provided") | |
2051 | port = kern.GetValueFromAddress(cmd_args[0], 'struct ipc_port *') | |
2052 | i = 1 | |
2053 | ||
d9a64523 | 2054 | return FindPortRights(cmd_args=[unsigned(port)], cmd_options={'-R':'S'}) |
39037602 | 2055 | |
39037602 A |
2056 | |
2057 | @lldb_command('showtasksuspenders') | |
2058 | def ShowTaskSuspenders(cmd_args=[], cmd_options={}): | |
2059 | """ Display the tasks and send rights that are holding a target task suspended. | |
2060 | Usage: (lldb) showtasksuspenders <task_t> | |
2061 | """ | |
2062 | if not cmd_args: | |
2063 | raise ArgumentError("no task address provided") | |
2064 | task = kern.GetValueFromAddress(cmd_args[0], 'task_t') | |
2065 | ||
2066 | if task.suspend_count == 0: | |
2067 | print "task {:#x} ({:s}) is not suspended".format(unsigned(task), Cast(task.bsd_info, 'proc_t').p_name) | |
2068 | return | |
2069 | ||
2070 | # If the task has been suspended by the kernel (potentially by | |
2071 | # kperf, using task_suspend_internal) or a client of task_suspend2 | |
2072 | # that does not convert its task suspension token to a port using | |
2073 | # convert_task_suspension_token_to_port, then it's impossible to determine | |
2074 | # which task did the suspension. | |
2075 | port = task.itk_resume | |
2076 | if not port: | |
2077 | print "task {:#x} ({:s}) is suspended but no resume port exists".format(unsigned(task), Cast(task.bsd_info, 'proc_t').p_name) | |
2078 | return | |
2079 | ||
d9a64523 | 2080 | return FindPortRights(cmd_args=[unsigned(port)], cmd_options={'-R':'S'}) |