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