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