1 2""" Please make sure you read the README file COMPLETELY BEFORE reading anything below. 3 It is very critical that you read coding guidelines in Section E in README file. 4""" 5from typing import Union, Optional 6from xnu import * 7import sys, shlex 8from utils import * 9from core.lazytarget import * 10import time 11import xnudefines 12import kmemory 13import memory 14import json 15 16from collections import defaultdict, namedtuple 17 18NO_PROC_NAME = "unknown" 19P_LHASTASK = 0x00000002 20TF_HASPROC = 0x00800000 21 22THREAD_STATE_CHARS = { 23 0x0: '', 24 0x1: 'W', 25 0x2: 'S', 26 0x4: 'R', 27 0x8: 'U', 28 0x10: 'H', 29 0x20: 'A', 30 0x40: 'P', 31 0x80: 'I', 32 0x100: 'K' 33} 34LAST_THREAD_STATE = 0x100 35 36def GetProcPID(proc): 37 """ returns the PID of a process. 38 params: 39 proc: value object representing a proc in the kernel. 40 returns: 41 int: the pid of the process. 42 """ 43 return unsigned(proc.p_pid) if proc is not None else -1 44 45def GetProcPlatform(proc): 46 """ returns the platform identifier of a process. 47 params: 48 proc: value object representing a proc in the kernel. 49 returns: 50 int: the platform identifier of the process. 51 """ 52 if not proc: 53 return None 54 return int(proc.p_proc_ro.p_platform_data.p_platform) 55 56def GetProcName(proc): 57 """ returns a string name of the process. Longer variant is preffered if provided. 58 params: 59 proc: value object representing a proc in the kernel. 60 returns: 61 str: a string name of the process linked to the task. 62 """ 63 if proc is None: 64 return NO_PROC_NAME 65 name = str(proc.p_name) 66 return name if name != '' else str(proc.p_comm) 67 68def GetProcNameForTask(task): 69 """ returns a string name of the process. If proc is not valid the proc 70 name is looked up in the associated importance structure (if 71 available). If no name can be found, "unknown" is returned. 72 params: 73 task: value object represeting a task in the kernel. 74 returns: 75 str : A string name of the process linked to the task 76 """ 77 if task: 78 p = GetProcFromTask(task) 79 if p is not None: 80 return GetProcName(p) 81 82 if ((task_imp_base := get_field(task, 'task_imp_base')) is not None and 83 (iit_procname := get_field(task_imp_base, 'iit_procname')) is not None and 84 unsigned(task_imp_base) != 0): 85 return str(iit_procname) 86 87 return NO_PROC_NAME 88 89def GetProcPIDForTask(task): 90 """ returns a int pid of the process. if the proc is not valid, val[5] from audit_token is returned. 91 params: 92 task: value object representing a task in the kernel 93 returns: 94 int : pid of the process or -1 if not found 95 """ 96 if not task: 97 return -1 98 99 p = GetProcFromTask(task) 100 if p is not None: 101 return GetProcPID(p) 102 103 proc_ro = Cast(task.bsd_info_ro, 'proc_ro *') 104 pid = unsigned(proc_ro.task_tokens.audit_token.val[5]) 105 return pid 106 107def GetProcStartAbsTimeForTask(task): 108 if task: 109 p = GetProcFromTask(task) 110 if p is not None: 111 return p.p_stats.ps_start 112 return None 113 114def GetProcInfo(proc): 115 """ returns a string name, pid, parent and task for a proc_t. Decodes cred, flag and p_stat fields. 116 params: 117 proc : value object representing a proc in the kernel 118 returns: 119 str : A string describing various information for process. 120 """ 121 out_string = "" 122 out_string += ("Process {p: <#020x}\n\tname {0: <32s}\n\tpid:{1: <6d} " + 123 "task:{task: <#020x} p_stat:{p.p_stat: <6d} parent pid: {p.p_ppid: <6d}\n" 124 ).format(GetProcName(proc), GetProcPID(proc), task=GetTaskFromProc(proc), p=proc) 125 #print the Creds 126 ucred = proc.p_proc_ro.p_ucred.__smr_ptr 127 if ucred: 128 out_string += "Cred: euid {:d} ruid {:d} svuid {:d}\n".format(ucred.cr_posix.cr_uid, 129 ucred.cr_posix.cr_ruid, 130 ucred.cr_posix.cr_svuid ) 131 #print the flags 132 flags = int(proc.p_flag) 133 out_string += "Flags: {0: <#020x}\n".format(flags) 134 num = 1 135 while num <= flags: 136 if flags & num: 137 explain_str = xnudefines.proc_flag_explain_strings.get(num, 'unknown') 138 out_string += "\t0x{:08x} - ".format(num) + explain_str + "\n" 139 elif num == 0x4: #special case for 32bit flag 140 out_string += "\t!0x00000004 - process is 32 bit\n" 141 num = num << 1 142 out_string += "State: " 143 state_val = proc.p_stat 144 if state_val < 1 or state_val > len(xnudefines.proc_state_strings) : 145 out_string += "(Unknown)" 146 else: 147 out_string += xnudefines.proc_state_strings[int(state_val)] 148 149 return out_string 150 151def GetProcNameForPid(pid): 152 """ Finds the name of the process corresponding to a given pid 153 params: 154 pid : int, pid you want to find the procname for 155 returns 156 str : Name of the process corresponding to the pid, "Unknown" if not found 157 """ 158 for p in kern.procs: 159 if int(GetProcPID(p)) == int(pid): 160 return GetProcName(p) 161 return NO_PROC_NAME 162 163def GetProcForPid(search_pid): 164 """ Finds the value object representing a proc in the kernel based on its pid 165 params: 166 search_pid : int, pid whose proc structure you want to find 167 returns: 168 value : The value object representing the proc, if a proc corresponding 169 to the given pid is found. Returns None otherwise 170 """ 171 if search_pid == 0: 172 return kern.globals.initproc 173 else: 174 headp = kern.globals.allproc 175 for proc in IterateListEntry(headp, 'p_list'): 176 if GetProcPID(proc) == search_pid: 177 return proc 178 return None 179 180@lldb_command('allproc') 181def AllProc(cmd_args=None): 182 """ Walk through the allproc structure and print procinfo for each process structure. 183 params: 184 cmd_args - [] : array of strings passed from lldb command prompt 185 """ 186 for proc in kern.procs : 187 print(GetProcInfo(proc)) 188 189 190@lldb_command('zombproc') 191def ZombProc(cmd_args=None): 192 """ Routine to print out all procs in the zombie list 193 params: 194 cmd_args - [] : array of strings passed from lldb command prompt 195 """ 196 if any(kern.zombprocs): 197 print("\nZombie Processes:") 198 for proc in kern.zombprocs: 199 print(GetProcInfo(proc) + "\n\n") 200 201@lldb_command('zombtasks') 202def ZombTasks(cmd_args=None): 203 """ Routine to print out all tasks in the zombie list 204 params: None 205 """ 206 out_str = "" 207 if any(kern.zombprocs): 208 header = "\nZombie Tasks:\n" 209 header += GetTaskSummary.header + " " + GetProcSummary.header 210 for proc in kern.zombprocs: 211 if proc.p_stat != 5: 212 t = GetTaskFromProc(proc) 213 out_str += GetTaskSummary(t) +" "+ GetProcSummary(proc) + "\n" 214 if out_str != "": 215 print(header) 216 print(out_str) 217 218# Macro: zombstacks 219def ShowZombStacks(O=None, regex=None): 220 header_flag = 0 221 for proc in kern.zombprocs: 222 if proc.p_stat != 5: 223 if header_flag == 0: 224 print("\nZombie Stacks:") 225 header_flag = 1 226 t = GetTaskFromProc(proc) 227 if t is not None: 228 ShowTaskStacks(t, O=O, regex=regex) 229 230@lldb_command('zombstacks', fancy=True) 231def ZombStacksCommand(cmd_args=None, cmd_options={}, O=None): 232 """ Routine to print out all stacks of tasks that are exiting 233 """ 234 ShowZombStacks(O=O) 235# EndMacro: zombstacks 236 237 238""" AST Flags: 239 P - AST_PREEMPT 240 Q - AST_QUANTUM 241 U - AST_URGENT 242 H - AST_HANDOFF 243 Y - AST_YIELD 244 A - AST_APC 245 L - AST_LEDGER 246 B - AST_BSD 247 K - AST_KPERF 248 M - AST_MACF 249 r - AST_RESET_PCS 250 a - AST_ARCADE 251 X - AST_MACH_EXCEPTION 252 T - AST_TELEMETRY_USER 253 T - AST_TELEMETRY_KERNEL 254 T - AST_TELEMETRY_WINDOWED 255 S - AST_SFI 256 D - AST_DTRACE 257 I - AST_TELEMETRY_IO 258 E - AST_KEVENT 259 R - AST_REBALANCE 260 p - AST_PROC_RESOURCE 261 d - AST_DEBUG_ASSERT 262 T - AST_TELEMETRY_MACF 263""" 264_AST_CHARS = { 265 0x1: 'P', 0x2: 'Q', 0x4: 'U', 0x8: 'H', 0x10: 'Y', 0x20: 'A', 266 0x40: 'L', 0x80: 'B', 0x100: 'K', 0x200: 'M', 0x400: 'r', 0x800: 'a', 267 0x1000: 'X', 0x2000: 'T', 0x4000: 'T', 0x8000: 'T', 0x10000: 'S', 268 0x20000: 'D', 0x40000: 'I', 0x80000: 'E', 0x100000: 'R', 269 0x400000: 'p', 0x800000: 'd', 0x1000000: 'T' 270} 271 272 273def GetASTSummary(ast): 274 """ Summarizes an AST field """ 275 276 state = int(ast) 277 state_str = '' 278 279 for ast, char in _AST_CHARS.items(): 280 state_str += char if state & ast else '' 281 282 return state_str 283 284 285@lldb_type_summary(['kcdata_descriptor *', 'kcdata_descriptor_t']) 286@header("{0: <20s} {1: <20s} {2: <20s} {3: <10s} {4: <5s}".format("kcdata_descriptor", "begin_addr", "cur_pos", "size", "flags")) 287def GetKCDataSummary(kcdata): 288 """ Summarizes kcdata_descriptor structure 289 params: kcdata: value - value object representing kcdata_descriptor 290 returns: str - summary of the kcdata object 291 """ 292 format_string = "{0: <#020x} {1: <#020x} {2: <#020x} {3: <10d} {4: <#05x}" 293 return format_string.format(kcdata, kcdata.kcd_addr_begin, kcdata.kcd_addr_end, kcdata.kcd_length, kcdata.kcd_flags) 294 295INVALID_TASK_SUMMARY = "Process is not valid." 296 297@lldb_type_summary(['task', 'task_t']) 298@header("{0: <20s} {1: <20s} {2: <20s} {3: >5s} {4: <5s}".format("task","vm_map", "ipc_space", "#acts", "flags")) 299def GetTaskSummary(task: Optional[value], showcorpse=False) -> str: 300 """ Summarizes the important fields in task structure. 301 params: task: value - value object representing a task in kernel 302 returns: str - summary of the task 303 """ 304 if task is None: 305 return INVALID_TASK_SUMMARY 306 307 out_string = "" 308 format_string = '{0: <#020x} {1: <#020x} {2: <#020x} {3: >5d} {4: <5s}' 309 thread_count = int(task.thread_count) 310 task_flags = '' 311 if hasattr(task, "suppression_generation") and (int(task.suppression_generation) & 0x1) == 0x1: 312 task_flags += 'P' 313 if hasattr(task, "effective_policy") and int(task.effective_policy.tep_sup_active) == 1: 314 task_flags += 'N' 315 if hasattr(task, "suspend_count") and int(task.suspend_count) > 0: 316 task_flags += 'S' 317 if hasattr(task, 'task_imp_base') and unsigned(task.task_imp_base): 318 tib = task.task_imp_base 319 if int(tib.iit_receiver) == 1: 320 task_flags += 'R' 321 if int(tib.iit_donor) == 1: 322 task_flags += 'D' 323 if int(tib.iit_assertcnt) > 0: 324 task_flags += 'B' 325 326 proc_ro = Cast(task.bsd_info_ro, 'proc_ro *') 327 if unsigned(proc_ro) != 0: 328 # check if corpse flag is set 329 if unsigned(proc_ro.t_flags_ro) & 0x20: 330 task_flags += 'C' 331 332 if unsigned(task.t_flags) & 0x40: 333 task_flags += 'P' 334 335 out_string += format_string.format(task, task.map, task.itk_space, thread_count, task_flags) 336 if showcorpse is True and unsigned(task.corpse_info) != 0: 337 out_string += " " + GetKCDataSummary(task.corpse_info) 338 return out_string 339 340@header("{0: <28s}".format("task_role")) 341def GetTaskRoleSummary(task): 342 """ Summarizes task_role structure 343 params: task: value - value object representing task 344 returns: str - summary of the task role string 345 """ 346 format_string = "{0: <28s}" 347 task_role = task.effective_policy.tep_role 348 return format_string.format(GetTaskRoleString(task_role)) 349 350def GetMachThread(uthread): 351 """ Converts the passed in value interpreted as a uthread_t into a thread_t 352 """ 353 addr = unsigned(uthread) - sizeof('struct thread') 354 thread = kern.GetValueFromAddress(addr, 'struct thread *') 355 return thread 356 357def GetBSDThread(thread: Union[lldb.SBValue, value]) -> value: 358 """ Converts the passed in value interpreted as a thread_t into a uthread_t 359 """ 360 addr = unsigned(thread) + sizeof('struct thread') 361 return kern.CreateValueFromAddress(addr, 'struct uthread') 362 363def GetProcFromTask(task) -> Optional[value]: 364 """ Converts the passed in value interpreted as a task_t into a proc_t 365 """ 366 task_addr = unsigned(task) 367 if task_addr and unsigned(task.t_flags) & TF_HASPROC: 368 addr = task_addr - kern.globals.proc_struct_size 369 return value(task.GetSBValue().xCreateValueFromAddress( 370 'proc', addr, gettype('struct proc') 371 ).AddressOf()) 372 return None 373 374def GetTaskFromProc(proc) -> Optional[value]: 375 """ Converts the passed in value interpreted as a proc_t into a task_t 376 """ 377 proc_addr = unsigned(proc) 378 if proc_addr and unsigned(proc.p_lflag) & P_LHASTASK: 379 addr = proc_addr + kern.globals.proc_struct_size 380 return value(proc.GetSBValue().xCreateValueFromAddress( 381 'task', addr, gettype('struct task') 382 ).AddressOf()) 383 return None 384 385def GetThreadNameFromBSDThread(uthread): 386 """ Get the name of a thread from a BSD thread, if possible. 387 Returns the empty string otherwise. 388 """ 389 if int(uthread.pth_name) != 0 : 390 th_name_strval = Cast(uthread.pth_name, 'char *') 391 if len(str(th_name_strval)) > 0 : 392 return str(th_name_strval) 393 394 return '' 395 396def GetThreadName(thread): 397 """ Get the name of a thread, if possible. Returns the empty string 398 otherwise. 399 """ 400 uthread = GetBSDThread(thread) 401 return GetThreadNameFromBSDThread(uthread) 402 403ThreadSummary = namedtuple('ThreadSummary', [ 404 'thread', 'tid', 'task', 'processor', 'base', 'pri', 'sched_mode', 'io_policy', 405 'state', 'ast', 'waitq', 'wait_evt', 'wait_evt_sym', 'wait_msg', 406 'name']) 407ThreadSummaryNames = ThreadSummary(*ThreadSummary._fields) 408ThreadSummaryFormat = ( 409 '{ts.thread: <20s} {ts.tid: <10s} {ts.task: <20s} {ts.processor: <20s} {ts.base: <6s} ' 410 '{ts.pri: <6s} {ts.sched_mode: <10s} {ts.io_policy: <15s} ' 411 '{ts.state: <8s} {ts.ast: <12s} {ts.waitq: <18s} {ts.wait_evt: <18s} ' 412 '{ts.wait_evt_sym: <30s} {ts.wait_msg: <20s} {ts.name: <20s}') 413 414@lldb_type_summary(['thread *', 'thread_t']) 415@header(ThreadSummaryFormat.format(ts=ThreadSummaryNames)) 416def GetThreadSummary(thread, O=None): 417 """ Summarize the thread structure. 418 419 params: thread: value - value object representing a thread in kernel 420 returns: str - summary of a thread 421 422 State flags: 423 W - Wait asserted 424 S - Suspended 425 R - Runnable 426 U - Uninterruptible 427 H - Terminated 428 A - Terminated (on queue) 429 I - Idle thread 430 C - Crashed thread 431 K - Waking 432 433 policy flags: 434 B - darwinbg 435 T - IO throttle 436 P - IO passive 437 D - Terminated 438 """ 439 440 # Check that this is a valid thread (if possible). 441 if hasattr(thread, "thread_magic") and thread.thread_magic != 0x1234ABCDDCBA4321: 442 # Do not raise exception so iterators like showscheduler don't abort. 443 return f"{thread:<#018x} <invalid thread>" 444 445 thread_ptr_str = '{:<#018x}'.format(thread) 446 thread_task_ptr_str = '{:<#018x}'.format(thread.t_tro.tro_task) 447 448 if int(thread.static_param): 449 thread_ptr_str += ' W' 450 thread_id = hex(thread.thread_id) 451 processor = hex(thread.last_processor) 452 base_priority = str(int(thread.base_pri)) 453 sched_priority = str(int(thread.sched_pri)) 454 sched_mode = '' 455 mode = str(thread.sched_mode) 456 if 'TIMESHARE' in mode: 457 sched_mode += 'TMSHR' 458 elif 'FIXED' in mode: 459 sched_mode += 'FIXED' 460 elif 'REALTIME' in mode: 461 sched_mode += 'RT' 462 463 if (unsigned(thread.bound_processor) != 0): 464 sched_mode += ' BIND' 465 466 TH_SFLAG_THROTTLED = 0x4 467 if (unsigned(thread.sched_flags) & TH_SFLAG_THROTTLED): 468 sched_mode += ' BG' 469 470 uthread = GetBSDThread(thread) 471 thread_name = GetThreadNameFromBSDThread(uthread) 472 473 io_policy_str = "" 474 if int(uthread.uu_flag) & 0x400: 475 io_policy_str += 'RAGE ' 476 if int(thread.effective_policy.thep_darwinbg) != 0: 477 io_policy_str += 'B' 478 if int(thread.effective_policy.thep_io_tier) != 0: 479 io_policy_str += 'T' 480 if int(thread.effective_policy.thep_io_passive) != 0: 481 io_policy_str += 'P' 482 if int(thread.effective_policy.thep_terminated) != 0: 483 io_policy_str += 'D' 484 485 state = int(thread.state) 486 state_str = '' 487 mask = 0x1 488 while mask <= LAST_THREAD_STATE: 489 state_str += THREAD_STATE_CHARS[int(state & mask)] 490 mask <<= 1 491 492 if int(thread.inspection): 493 state_str += 'C' 494 495 ast = int(thread.ast) | int(thread.reason) 496 ast_str = GetASTSummary(ast) 497 498 wait_queue_str = '' 499 wait_event_str = '' 500 wait_event_str_sym = '' 501 wait_message = '' 502 if (state & 0x1) != 0: 503 wait_queue_str = '{:<#018x}'.format(unsigned(thread.waitq.wq_q)) 504 wait_event_str = '{:<#018x}'.format(unsigned(thread.wait_event)) 505 wait_event_str_sym = kern.Symbolicate(int(hex(thread.wait_event), 16)) 506 if int(uthread.uu_wmesg) != 0: 507 wait_message = str(Cast(uthread.uu_wmesg, 'char *')) 508 509 ts = ThreadSummary( 510 thread=thread_ptr_str, tid=thread_id, 511 task=thread_task_ptr_str, processor=processor, 512 base=base_priority, pri=sched_priority, sched_mode=sched_mode, 513 io_policy=io_policy_str, state=state_str, ast=ast_str, 514 waitq=wait_queue_str, wait_evt=wait_event_str, 515 wait_evt_sym=wait_event_str_sym, wait_msg=wait_message, 516 name=thread_name) 517 if O is not None: 518 return O.format(ThreadSummaryFormat, ts=ts) 519 else: 520 return ThreadSummaryFormat.format(ts=ts) 521 522 523def GetTaskRoleString(role): 524 role_strs = { 525 -1 : "TASK_RENICED", 526 0 : "TASK_UNSPECIFIED", 527 1 : "TASK_FOREGROUND_APPLICATION", 528 2 : "TASK_BACKGROUND_APPLICATION", 529 3 : "TASK_CONTROL_APPLICATION", 530 4 : "TASK_GRAPHICS_SERVER", 531 5 : "TASK_THROTTLE_APPLICATION", 532 6 : "TASK_NONUI_APPLICATION", 533 7 : "TASK_DEFAULT_APPLICATION", 534 8 : "TASK_DARWINBG_APPLICATION" 535 } 536 return role_strs[int(role)] 537 538def GetCoalitionFlagString(coal): 539 flags = [] 540 if (coal.privileged): 541 flags.append('privileged') 542 if (coal.termrequested): 543 flags.append('termrequested') 544 if (coal.terminated): 545 flags.append('terminated') 546 if (coal.reaped): 547 flags.append('reaped') 548 if (coal.notified): 549 flags.append('notified') 550 if (coal.efficient): 551 flags.append('efficient') 552 return "|".join(flags) 553 554def GetCoalitionTasks(queue, coal_type, thread_details=False): 555 sfi_strs = { 556 0x0 : "SFI_CLASS_UNSPECIFIED", 557 0x1 : "SFI_CLASS_DARWIN_BG", 558 0x2 : "SFI_CLASS_APP_NAP", 559 0x3 : "SFI_CLASS_MANAGED_FOCAL", 560 0x4 : "SFI_CLASS_MANAGED_NONFOCAL", 561 0x5 : "SFI_CLASS_DEFAULT_FOCAL", 562 0x6 : "SFI_CLASS_DEFAULT_NONFOCAL", 563 0x7 : "SFI_CLASS_KERNEL", 564 0x8 : "SFI_CLASS_OPTED_OUT", 565 0x9 : "SFI_CLASS_UTILITY", 566 0xA : "SFI_CLASS_LEGACY_FOCAL", 567 0xB : "SFI_CLASS_LEGACY_NONFOCAL", 568 0xC : "SFI_CLASS_USER_INITIATED_FOCAL", 569 0xD : "SFI_CLASS_USER_INITIATED_NONFOCAL", 570 0xE : "SFI_CLASS_USER_INTERACTIVE_FOCAL", 571 0xF : "SFI_CLASS_USER_INTERACTIVE_NONFOCAL", 572 0x10 : "SFI_CLASS_MAINTENANCE", 573 } 574 tasks = [] 575 field_path = '.task_coalition[{}]'.format(coal_type) 576 for task in IterateLinkageChain(queue, 'task *', field_path): 577 task_str = "({0: <d},{1: #x}, {2: <s}, {3: <s})".format(GetProcPIDForTask(task),task,GetProcNameForTask(task),GetTaskRoleString(task.effective_policy.tep_role)) 578 if thread_details: 579 for thread in IterateQueue(task.threads, "thread_t", "task_threads"): 580 task_str += "\n\t\t\t|-> thread:" + hex(thread) + ", " + sfi_strs[int(thread.sfi_class)] 581 tasks.append(task_str) 582 return tasks 583 584def GetCoalitionTypeString(type): 585 """ Convert a coalition type field into a string 586 Currently supported types (from <mach/coalition.h>): 587 COALITION_TYPE_RESOURCE 588 COALITION_TYPE_JETSAM 589 """ 590 if type == 0: # COALITION_TYPE_RESOURCE 591 return 'RESOURCE' 592 if type == 1: 593 return 'JETSAM' 594 return '<unknown>' 595 596def GetResourceCoalitionSummary(coal, verbose=False): 597 """ Summarize a resource coalition 598 """ 599 out_string = "Resource Coalition:\n\t Ledger:\n" 600 thread_details = False 601 if config['verbosity'] > vSCRIPT: 602 thread_details = True 603 ledgerp = coal.r.ledger 604 if verbose and unsigned(ledgerp) != 0: 605 i = 0 606 while i != ledgerp.l_template.lt_cnt: 607 out_string += "\t\t" 608 out_string += GetLedgerEntrySummary(kern.globals.task_ledger_template, ledgerp, i) 609 i = i + 1 610 out_string += "\t bytesread {0: <d}\n\t byteswritten {1: <d}\n\t gpu_time {2: <d}".format(coal.r.bytesread, coal.r.byteswritten, coal.r.gpu_time) 611 out_string += "\n\t total_tasks {0: <d}\n\t dead_tasks {1: <d}\n\t active_tasks {2: <d}".format(coal.r.task_count, coal.r.dead_task_count, coal.r.task_count - coal.r.dead_task_count) 612 out_string += "\n\t last_became_nonempty_time {0: <d}\n\t time_nonempty {1: <d}".format(coal.r.last_became_nonempty_time, coal.r.time_nonempty) 613 if verbose: 614 out_string += "\n\t cpu_time_effective[THREAD_QOS_DEFAULT] {0: <d}".format(coal.r.cpu_time_eqos[0]) 615 out_string += "\n\t cpu_time_effective[THREAD_QOS_MAINTENANCE] {0: <d}".format(coal.r.cpu_time_eqos[1]) 616 out_string += "\n\t cpu_time_effective[THREAD_QOS_BACKGROUND] {0: <d}".format(coal.r.cpu_time_eqos[2]) 617 out_string += "\n\t cpu_time_effective[THREAD_QOS_UTILITY] {0: <d}".format(coal.r.cpu_time_eqos[3]) 618 out_string += "\n\t cpu_time_effective[THREAD_QOS_LEGACY] {0: <d}".format(coal.r.cpu_time_eqos[4]) 619 out_string += "\n\t cpu_time_effective[THREAD_QOS_USER_INITIATED] {0: <d}".format(coal.r.cpu_time_eqos[5]) 620 out_string += "\n\t cpu_time_effective[THREAD_QOS_USER_INTERACTIVE] {0: <d}".format(coal.r.cpu_time_eqos[6]) 621 out_string += "\n\t Tasks:\n\t\t" 622 tasks = GetCoalitionTasks(addressof(coal.r.tasks), 0, thread_details) 623 out_string += "\n\t\t".join(tasks) 624 return out_string 625 626def GetJetsamCoalitionSummary(coal, verbose=False): 627 out_string = "Jetsam Coalition:" 628 thread_details = False 629 if config['verbosity'] > vSCRIPT: 630 thread_details = True 631 if unsigned(coal.j.leader) == 0: 632 out_string += "\n\t NO Leader!" 633 else: 634 out_string += "\n\t Leader:\n\t\t" 635 out_string += "({0: <d},{1: #x}, {2: <s}, {3: <s})".format(GetProcPIDForTask(coal.j.leader),coal.j.leader,GetProcNameForTask(coal.j.leader),GetTaskRoleString(coal.j.leader.effective_policy.tep_role)) 636 out_string += "\n\t Extensions:\n\t\t" 637 tasks = GetCoalitionTasks(addressof(coal.j.extensions), 1, thread_details) 638 out_string += "\n\t\t".join(tasks) 639 out_string += "\n\t XPC Services:\n\t\t" 640 tasks = GetCoalitionTasks(addressof(coal.j.services), 1, thread_details) 641 out_string += "\n\t\t".join(tasks) 642 out_string += "\n\t Other Tasks:\n\t\t" 643 tasks = GetCoalitionTasks(addressof(coal.j.other), 1, thread_details) 644 out_string += "\n\t\t".join(tasks) 645 out_string += "\n\t Thread Group: {0: <#020x}\n".format(coal.j.thread_group) 646 return out_string 647 648@lldb_type_summary(['coalition_t', 'coalition *']) 649@header("{0: <20s} {1: <15s} {2: <10s} {3: <10s} {4: <10s} {5: <12s} {6: <12s} {7: <20s}".format("coalition", "type", "id", "ref count", "act count", "focal cnt", "nonfocal cnt","flags")) 650def GetCoalitionSummary(coal): 651 if unsigned(coal) == 0: 652 return '{0: <#020x} {1: <15s} {2: <10d} {3: <10d} {4: <10d} {5: <12d} {6: <12d} {7: <s}'.format(0, "", -1, -1, -1, -1, -1, "") 653 out_string = "" 654 format_string = '{0: <#020x} {1: <15s} {2: <10d} {3: <10d} {4: <10d} {5: <12d} {6: <12d} {7: <s}' 655 type_string = GetCoalitionTypeString(coal.type) 656 flag_string = GetCoalitionFlagString(coal) 657 out_string += format_string.format(coal, type_string, coal.id, coal.ref_count, coal.active_count, coal.focal_task_count, coal.nonfocal_task_count, flag_string) 658 return out_string 659 660def GetCoalitionInfo(coal, verbose=False): 661 """ returns a string describing a coalition, including details about the particular coalition type. 662 params: 663 coal : value object representing a coalition in the kernel 664 returns: 665 str : A string describing the coalition. 666 """ 667 if unsigned(coal) == 0: 668 return "<null coalition>" 669 typestr = GetCoalitionTypeString(coal.type) 670 flagstr = GetCoalitionFlagString(coal) 671 out_string = "" 672 out_string += "Coalition {c: <#020x}\n\tID {c.id: <d}\n\tType {c.type: <d} ({t: <s})\n\tRefCount {c.ref_count: <d}\n\tActiveCount {c.active_count: <d}\n\tFocal Tasks: {c.focal_task_count: <d}\n\tNon-Focal Tasks: {c.nonfocal_task_count: <d}\n\tFlags {f: <s}\n\t".format(c=coal,t=typestr,f=flagstr) 673 if coal.type == 0: # COALITION_TYPE_RESOURCE 674 out_string += GetResourceCoalitionSummary(coal, verbose) 675 elif coal.type == 1: # COALITION_TYPE_JETSAM 676 out_string += GetJetsamCoalitionSummary(coal, verbose) 677 else: 678 out_string += "Unknown Type" 679 680 return out_string 681 682# Macro: showcoalitioninfo 683 684@lldb_command('showcoalitioninfo') 685def ShowCoalitionInfo(cmd_args=None, cmd_options={}): 686 """ Display more detailed information about a coalition 687 Usage: showcoalitioninfo <address of coalition> 688 """ 689 verbose = False 690 if config['verbosity'] > vHUMAN: 691 verbose = True 692 if cmd_args is None or len(cmd_args) == 0: 693 raise ArgumentError("No arguments passed") 694 coal = kern.GetValueFromAddress(cmd_args[0], 'coalition *') 695 if not coal: 696 print("unknown arguments:", str(cmd_args)) 697 return False 698 print(GetCoalitionInfo(coal, verbose)) 699 700# EndMacro: showcoalitioninfo 701 702# Macro: showallcoalitions 703 704@lldb_command('showallcoalitions') 705def ShowAllCoalitions(cmd_args=None): 706 """ Print a summary listing of all the coalitions 707 """ 708 global kern 709 print(GetCoalitionSummary.header) 710 for c in kern.coalitions: 711 print(GetCoalitionSummary(c)) 712 713# EndMacro: showallcoalitions 714 715# Macro: showcurrentforegroundapps 716 717@lldb_command('showcurrentforegroundapps') 718def ShowCurrentForegroundStatus(cmd_args=None): 719 """ Print current listing of foreground applications 720 """ 721 global kern 722 print(GetTaskSummary.header + " " + GetProcSummary.header + " " + GetTaskRoleSummary.header) 723 for t in kern.tasks: 724 task_role = t.effective_policy.tep_role 725 # Only print tasks where tep_role is 'TASK_FOREGROUND_APPLICATION = 1' 726 if (task_role == 1): 727 pval = GetProcFromTask(t) 728 print(GetTaskSummary(t), GetProcSummary(pval), GetTaskRoleSummary(t)) 729 730# EndMacro: showcurrentforegroundapps 731 732# Macro: showallthreadgroups 733 734@lldb_type_summary(['struct thread_group *', 'thread_group *']) 735@header("{0: <20s} {1: <5s} {2: <16s} {3: <5s} {4: <8s} {5: <20s}".format("thread_group", "id", "name", "refc", "flags", "recommendation")) 736def GetThreadGroupSummary(tg): 737 if unsigned(tg) == 0: 738 return '{0: <#020x} {1: <5d} {2: <16s} {3: <5d} {4: <8s} {5: <20d}'.format(0, -1, "", -1, "", -1) 739 out_string = "" 740 format_string = '{0: <#020x} {1: <5d} {2: <16s} {3: <5d} {4: <8s} {5: <20d}' 741 tg_flags = '' 742 if (tg.tg_flags & 0x1): 743 tg_flags += 'E' 744 if (tg.tg_flags & 0x2): 745 tg_flags += 'A' 746 if (tg.tg_flags & 0x4): 747 tg_flags += 'C' 748 if (tg.tg_flags & 0x100): 749 tg_flags += 'U' 750 out_string += format_string.format(tg, tg.tg_id, tg.tg_name, tg.tg_refcount.ref_count, tg_flags, tg.tg_recommendation) 751 return out_string 752 753@lldb_command('showallthreadgroups') 754def ShowAllThreadGroups(cmd_args=None): 755 """ Print a summary listing of all thread groups 756 """ 757 global kern 758 print(GetThreadGroupSummary.header) 759 for tg in kern.thread_groups: 760 print(GetThreadGroupSummary(tg)) 761 762# EndMacro: showallthreadgroups 763 764# Macro: showtaskcoalitions 765 766@lldb_command('showtaskcoalitions', 'F:') 767def ShowTaskCoalitions(cmd_args=None, cmd_options={}): 768 """ 769 """ 770 task_list = [] 771 if "-F" in cmd_options: 772 task_list = FindTasksByName(cmd_options["-F"]) 773 elif cmd_args: 774 t = kern.GetValueFromAddress(cmd_args[0], 'task *') 775 task_list.append(t) 776 else: 777 raise ArgumentError("No arguments passed") 778 779 if len(task_list) > 0: 780 print(GetCoalitionSummary.header) 781 for task in task_list: 782 print(GetCoalitionSummary(task.coalition[0])) 783 print(GetCoalitionSummary(task.coalition[1])) 784 785# EndMacro: showtaskcoalitions 786 787INVALID_PROC_SUMMARY = "Process is not valid." 788 789@lldb_type_summary(['proc', 'proc *']) 790@header("{0: >6s} {1: <18s} {2: >11s} {3: ^10s} {4: <32s}".format("pid", "process", "io_policy", "wq_state", "command")) 791def GetProcSummary(proc: Optional[value]) -> str: 792 """ Summarize the process data. 793 params: 794 proc : value - value representaitng a proc * in kernel 795 returns: 796 str - string summary of the process. 797 """ 798 if proc is None: 799 return INVALID_PROC_SUMMARY 800 801 out_string = "" 802 format_string= "{0: >6d} {1: <#018x} {2: >11s} {3: >2d} {4: >2d} {5: >2d} {6: <32s}" 803 pval = proc.GetSBValue() 804 #code.interact(local=locals()) 805 if str(pval.GetType()) != str(gettype('proc *')) : 806 return "Unknown type " + str(pval.GetType()) + " " + str(hex(proc)) 807 pid = int(GetProcPID(proc)) 808 proc_addr = int(hex(proc), 16) 809 proc_rage_str = "" 810 if int(proc.p_lflag) & 0x400000 : 811 proc_rage_str = "RAGE" 812 813 task = GetTaskFromProc(proc) 814 if task is None: 815 return "Process is not associated with a Task" 816 817 io_policy_str = "" 818 819 if int(task.effective_policy.tep_darwinbg) != 0: 820 io_policy_str += "B" 821 if int(task.effective_policy.tep_lowpri_cpu) != 0: 822 io_policy_str += "L" 823 824 if int(task.effective_policy.tep_io_tier) != 0: 825 io_policy_str += "T" 826 if int(task.effective_policy.tep_io_passive) != 0: 827 io_policy_str += "P" 828 if int(task.effective_policy.tep_terminated) != 0: 829 io_policy_str += "D" 830 831 if int(task.effective_policy.tep_latency_qos) != 0: 832 io_policy_str += "Q" 833 if int(task.effective_policy.tep_sup_active) != 0: 834 io_policy_str += "A" 835 836 if int(proc.p_refcount) & GetEnumValue("proc_ref_bits_t::P_REF_SHADOW") : 837 io_policy_str += "S" 838 839 840 try: 841 work_queue = proc.p_wqptr 842 if proc.p_wqptr != 0 : 843 wq_num_threads = int(work_queue.wq_nthreads) 844 wq_idle_threads = int(work_queue.wq_thidlecount) 845 wq_req_threads = int(work_queue.wq_reqcount) 846 else: 847 wq_num_threads = 0 848 wq_idle_threads = 0 849 wq_req_threads = 0 850 except: 851 wq_num_threads = -1 852 wq_idle_threads = -1 853 wq_req_threads = -1 854 process_name = GetProcName(proc) 855 if process_name == 'xpcproxy': 856 for thread in IterateQueue(task.threads, 'thread *', 'task_threads'): 857 thread_name = GetThreadName(thread) 858 if thread_name: 859 process_name += ' (' + thread_name + ')' 860 break 861 out_string += format_string.format(pid, proc_addr, " ".join([proc_rage_str, io_policy_str]), wq_num_threads, wq_idle_threads, wq_req_threads, process_name) 862 return out_string 863 864@lldb_type_summary(['tty_dev_t', 'tty_dev_t *']) 865@header("{0: <20s} {1: <10s} {2: <10s} {3: <15s} {4: <15s} {5: <15s} {6: <15s}".format("tty_dev","primary", "replica", "open", "free", "name", "revoke")) 866def GetTTYDevSummary(tty_dev): 867 """ Summarizes the important fields in tty_dev_t structure. 868 params: tty_dev: value - value object representing a tty_dev_t in kernel 869 returns: str - summary of the tty_dev 870 """ 871 out_string = "" 872 format_string = "{0: <#020x} {1: <#010x} {2: <#010x} {3: <15s} {4: <15s} {5: <15s} {6: <15s}" 873 open_fn = kern.Symbolicate(int(hex(tty_dev.open), 16)) 874 free_fn = kern.Symbolicate(int(hex(tty_dev.free), 16)) 875 name_fn = kern.Symbolicate(int(hex(tty_dev.name), 16)) 876 revoke_fn = kern.Symbolicate(int(hex(tty_dev.revoke), 16)) 877 out_string += format_string.format(tty_dev, tty_dev.primary, tty_dev.replica, open_fn, free_fn, name_fn, revoke_fn) 878 return out_string 879 880# Macro: showtask 881 882@lldb_command('showtask', 'F:') 883def ShowTask(cmd_args=None, cmd_options={}): 884 """ Routine to print a summary listing of given task 885 Usage: showtask <address of task> 886 or : showtask -F <name of task> 887 """ 888 task_list = [] 889 if "-F" in cmd_options: 890 task_list = FindTasksByName(cmd_options['-F']) 891 else: 892 if cmd_args is None or len(cmd_args) == 0: 893 raise ArgumentError("Invalid arguments passed.") 894 895 task_address = ArgumentStringToInt(cmd_args[0]) 896 tval = addressof(kern.CreateValueFromAddress(task_address, 'task')) 897 if not tval: 898 raise ArgumentError("Unknown arguments: {:s}".format(cmd_args[0])) 899 900 task_list.append(tval) 901 902 for tval in task_list: 903 print(GetTaskSummary.header + " " + GetProcSummary.header) 904 pval = GetProcFromTask(tval) 905 print(GetTaskSummary(tval) +" "+ GetProcSummary(pval)) 906 907# EndMacro: showtask 908 909# Macro: showpid 910 911@lldb_command('showpid') 912def ShowPid(cmd_args=None): 913 """ Routine to print a summary listing of task corresponding to given pid 914 Usage: showpid <pid value> 915 """ 916 if cmd_args is None or len(cmd_args) == 0: 917 raise ArgumentError("No arguments passed") 918 pidval = ArgumentStringToInt(cmd_args[0]) 919 for t in kern.tasks: 920 pval = GetProcFromTask(t) 921 if pval is not None and GetProcPID(pval) == pidval: 922 print(GetTaskSummary.header + " " + GetProcSummary.header) 923 print(GetTaskSummary(t) + " " + GetProcSummary(pval)) 924 break 925 926# EndMacro: showpid 927 928# Macro: showproc 929 930@lldb_command('showproc') 931def ShowProc(cmd_args=None): 932 """ Routine to print a summary listing of task corresponding to given proc 933 Usage: showproc <address of proc> 934 """ 935 if cmd_args is None or len(cmd_args) == 0: 936 raise ArgumentError("No arguments passed") 937 pval = kern.GetValueFromAddress(cmd_args[0], 'proc *') 938 if not pval: 939 print("unknown arguments:", str(cmd_args)) 940 return False 941 print(GetTaskSummary.header + " " + GetProcSummary.header) 942 tval = GetTaskFromProc(pval) 943 print(GetTaskSummary(tval) + " " + GetProcSummary(pval)) 944 945# EndMacro: showproc 946 947# Macro: showprocinfo 948 949@lldb_command('showprocinfo') 950def ShowProcInfo(cmd_args=None): 951 """ Routine to display name, pid, parent & task for the given proc address 952 It also shows the Cred, Flags and state of the process 953 Usage: showprocinfo <address of proc> 954 """ 955 if cmd_args is None or len(cmd_args) == 0: 956 raise ArgumentError("No arguments passed") 957 pval = kern.GetValueFromAddress(cmd_args[0], 'proc *') 958 if not pval: 959 print("unknown arguments:", str(cmd_args)) 960 return False 961 print(GetProcInfo(pval)) 962 963# EndMacro: showprocinfo 964 965#Macro: showprocfiles 966 967@lldb_command('showprocfiles') 968def ShowProcFiles(cmd_args=None): 969 """ Given a proc_t pointer, display the list of open file descriptors for the referenced process. 970 Usage: showprocfiles <proc_t> 971 """ 972 if cmd_args is None or len(cmd_args) == 0: 973 raise ArgumentError() 974 975 proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t') 976 proc_filedesc = addressof(proc.p_fd) 977 proc_ofiles = proc_filedesc.fd_ofiles 978 if unsigned(proc_ofiles) == 0: 979 print('No open files for proc {0: <s}'.format(cmd_args[0])) 980 return 981 print("{0: <5s} {1: <18s} {2: <10s} {3: <8s} {4: <18s} {5: <64s}".format('FD', 'FILEGLOB', 'FG_FLAGS', 'FG_TYPE', 'FG_DATA','INFO')) 982 print("{0:-<5s} {0:-<18s} {0:-<10s} {0:-<8s} {0:-<18s} {0:-<64s}".format("")) 983 984 for fd in range(0, unsigned(proc_filedesc.fd_afterlast)): 985 if unsigned(proc_ofiles[fd]) != 0: 986 out_str = '' 987 proc_fd_flags = proc_ofiles[fd].fp_flags 988 proc_fd_fglob = proc_ofiles[fd].fp_glob 989 proc_fd_fglob_fg_data = Cast(proc_fd_fglob.fg_data, 'void *') 990 out_str += "{0: <5d} ".format(fd) 991 out_str += "{0: <#18x} ".format(unsigned(proc_fd_fglob)) 992 out_str += "0x{0:0>8x} ".format(unsigned(proc_fd_flags)) 993 proc_fd_ftype = unsigned(proc_fd_fglob.fg_ops.fo_type) 994 if proc_fd_ftype in xnudefines.filetype_strings: 995 out_str += "{0: <8s} ".format(xnudefines.filetype_strings[proc_fd_ftype]) 996 else: 997 out_str += "?: {0: <5d} ".format(proc_fd_ftype) 998 out_str += "{0: <#18x} ".format(unsigned(proc_fd_fglob_fg_data)) 999 if proc_fd_ftype == 1: 1000 fd_name = Cast(proc_fd_fglob_fg_data, 'struct vnode *').v_name 1001 out_str += "{0: <64s}".format(fd_name) 1002 out_str += "\n" 1003 print(out_str) 1004 1005#EndMacro: showprocfiles 1006 1007#Macro: showtty 1008 1009@lldb_command('showtty') 1010def ShowTTY(cmd_args=None): 1011 """ Display information about a struct tty 1012 Usage: showtty <tty struct> 1013 """ 1014 if cmd_args is None or len(cmd_args) == 0: 1015 raise ArgumentError() 1016 1017 1018 tty = kern.GetValueFromAddress(cmd_args[0], 'struct tty *') 1019 print("TTY structure at: {0: <s}".format(cmd_args[0])) 1020 print("Last input to raw queue: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_rawq.c_cs), tty.t_rawq.c_cs)) 1021 print("Last input to canonical queue: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_canq.c_cs), tty.t_canq.c_cs)) 1022 print("Last output data: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_outq.c_cs), tty.t_outq.c_cs)) 1023 tty_state_info = [ 1024 ['', 'TS_SO_OLOWAT (Wake up when output <= low water)'], 1025 ['- (synchronous I/O mode)', 'TS_ASYNC (async I/O mode)'], 1026 ['', 'TS_BUSY (Draining output)'], 1027 ['- (Carrier is NOT present)', 'TS_CARR_ON (Carrier is present)'], 1028 ['', 'TS_FLUSH (Outq has been flushed during DMA)'], 1029 ['- (Open has NOT completed)', 'TS_ISOPEN (Open has completed)'], 1030 ['', 'TS_TBLOCK (Further input blocked)'], 1031 ['', 'TS_TIMEOUT (Wait for output char processing)'], 1032 ['', 'TS_TTSTOP (Output paused)'], 1033 ['', 'TS_WOPEN (Open in progress)'], 1034 ['', 'TS_XCLUDE (Tty requires exclusivity)'], 1035 ['', 'TS_BKSL (State for lowercase \\ work)'], 1036 ['', 'TS_CNTTB (Counting tab width, ignore FLUSHO)'], 1037 ['', 'TS_ERASE (Within a \\.../ for PRTRUB)'], 1038 ['', 'TS_LNCH (Next character is literal)'], 1039 ['', 'TS_TYPEN (Retyping suspended input (PENDIN))'], 1040 ['', 'TS_CAN_BYPASS_L_RINT (Device in "raw" mode)'], 1041 ['- (Connection NOT open)', 'TS_CONNECTED (Connection open)'], 1042 ['', 'TS_SNOOP (Device is being snooped on)'], 1043 ['', 'TS_SO_OCOMPLETE (Wake up when output completes)'], 1044 ['', 'TS_ZOMBIE (Connection lost)'], 1045 ['', 'TS_CAR_OFLOW (For MDMBUF - handle in driver)'], 1046 ['', 'TS_CTS_OFLOW (For CCTS_OFLOW - handle in driver)'], 1047 ['', 'TS_DSR_OFLOW (For CDSR_OFLOW - handle in driver)'] 1048 ] 1049 index = 0 1050 mask = 0x1 1051 tty_state = unsigned(tty.t_state) 1052 print("State:") 1053 while index < 24: 1054 if tty_state & mask != 0: 1055 if len(tty_state_info[index][1]) > 0: 1056 print('\t' + tty_state_info[index][1]) 1057 else: 1058 if len(tty_state_info[index][0]) > 0: 1059 print('\t' + tty_state_info[index][0]) 1060 index += 1 1061 mask = mask << 1 1062 print("Flags: 0x{0:0>8x}".format(unsigned(tty.t_flags))) 1063 print("Foreground Process Group: 0x{0:0>16x}".format(unsigned(tty.t_pgrp))) 1064 print("Enclosing session: 0x{0:0>16x}".format(unsigned(tty.t_session))) 1065 print("Termios:") 1066 print("\tInput Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_iflag))) 1067 print("\tOutput Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_oflag))) 1068 print("\tControl Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_cflag))) 1069 print("\tLocal Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_lflag))) 1070 print("\tInput Speed: {0: <8d}".format(tty.t_termios.c_ispeed)) 1071 print("\tOutput Speed: {0: <8d}".format(tty.t_termios.c_ospeed)) 1072 print("High Watermark: {0: <d} bytes".format(tty.t_hiwat)) 1073 print("Low Watermark : {0: <d} bytes".format(tty.t_lowat)) 1074 1075#EndMacro: showtty 1076 1077#Macro showallttydevs 1078 1079@lldb_command('showallttydevs') 1080def ShowAllTTYDevs(cmd_args=[], cmd_options={}): 1081 """ Show a list of ttydevs registered in the system. 1082 Usage: 1083 (lldb)showallttydevs 1084 """ 1085 tty_dev_head = kern.globals.tty_dev_head 1086 tty_dev = tty_dev_head 1087 print(GetTTYDevSummary.header) 1088 while unsigned(tty_dev) != 0: 1089 print(GetTTYDevSummary(tty_dev)) 1090 tty_dev = tty_dev.next 1091 return "" 1092 1093#EndMacro: showallttydevs 1094 1095#Macro: dumpthread_terminate_queue 1096 1097@lldb_command('dumpthread_terminate_queue', fancy=True) 1098def DumpThreadTerminateQueue(cmd_args=None, cmd_options={}, O=None): 1099 """ Displays the contents of the specified call_entry queue. 1100 Usage: dumpthread_terminate_queue 1101 """ 1102 1103 count = 0 1104 with O.table(GetThreadSummary.header): 1105 for th in IterateMPSCQueue(addressof(kern.globals.thread_terminate_queue.mpd_queue), 'struct thread', 'mpsc_links'): 1106 print(GetThreadSummary(th, O=O)) 1107 count += 1 1108 print("{0: <d} entries!".format(count)) 1109 1110#EndMacro: dumpthread_terminate_queue 1111 1112#Macro: dumpcrashed_thread_queue 1113 1114@lldb_command('dumpcrashed_thread_queue', fancy=True) 1115def DumpCrashedThreadsQueue(cmd_args=None, cmd_options={}, O=None): 1116 """ Displays the contents of the specified call_entry queue. 1117 Usage: dumpcrashed_thread_queue 1118 """ 1119 1120 count = 0 1121 with O.table(GetThreadSummary.header): 1122 for th in IterateQueue(addressof(kern.globals.crashed_threads_queue), 'struct thread *', 'q_link'): 1123 print(GetThreadSummary(th), O=O) 1124 count += 1 1125 print("{0: <d} entries!".format(count)) 1126 1127#EndMacro: dumpcrashed_thread_queue 1128 1129#Macro: dumpcallqueue 1130 1131@lldb_command('dumpcallqueue') 1132def DumpCallQueue(cmd_args=None): 1133 """ Displays the contents of the specified call_entry queue. 1134 Usage: dumpcallqueue <queue_head_t *> 1135 """ 1136 if cmd_args is None or len(cmd_args) == 0: 1137 raise ArgumentError("Invalid arguments") 1138 1139 print("{0: <18s} {1: <18s} {2: <18s} {3: <64s} {4: <18s}".format('CALL_ENTRY', 'PARAM0', 'PARAM1', 'DEADLINE', 'FUNC')) 1140 callhead = kern.GetValueFromAddress(cmd_args[0], 'queue_head_t *') 1141 count = 0 1142 for callentry in IterateQueue(callhead, 'struct call_entry *', 'q_link'): 1143 print("{0: <#18x} {1: <#18x} {2: <#18x} {3: <64d} {4: <#18x}".format( 1144 unsigned(callentry), unsigned(callentry.param0), unsigned(callentry.param1), 1145 unsigned(callentry.deadline), unsigned(callentry.func))) 1146 count += 1 1147 print("{0: <d} entries!".format(count)) 1148 1149#EndMacro: dumpcallqueue 1150 1151@lldb_command('showalltasklogicalwrites') 1152def ShowAllTaskIOStats(cmd_args=None): 1153 """ Commad to print I/O stats for all tasks 1154 """ 1155 print("{0: <20s} {1: <20s} {2: <20s} {3: <20s} {4: <20s} {5: <20s} {6: <20s} {7: <20s} {8: <20s} {9: <32}".format("task", "Immediate Writes", "Deferred Writes", "Invalidated Writes", "Metadata Writes", "Immediate Writes to External", "Deferred Writes to External", "Invalidated Writes to External", "Metadata Writes to External", "name")) 1156 for t in kern.tasks: 1157 pval = GetProcFromTask(t) 1158 print("{0: <#18x} {1: >20d} {2: >20d} {3: >20d} {4: >20d} {5: <20s} {6: <20s} {7: <20s} {8: <20s} {9: <20s}".format(t, 1159 t.task_writes_counters_internal.task_immediate_writes, 1160 t.task_writes_counters_internal.task_deferred_writes, 1161 t.task_writes_counters_internal.task_invalidated_writes, 1162 t.task_writes_counters_internal.task_metadata_writes, 1163 t.task_writes_counters_external.task_immediate_writes, 1164 t.task_writes_counters_external.task_deferred_writes, 1165 t.task_writes_counters_external.task_invalidated_writes, 1166 t.task_writes_counters_external.task_metadata_writes, 1167 GetProcName(pval))) 1168 1169 1170@lldb_command('showalltasks','C R', fancy=True) 1171def ShowAllTasks(cmd_args=None, cmd_options={}, O=None): 1172 """ Routine to print a summary listing of all the tasks 1173 wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" 1174 if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung 1175 io_policy -> RAGE - rapid aging of vnodes requested 1176 NORM - normal I/O explicitly requested (this is the default) 1177 PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) 1178 THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) 1179 Usage: (lldb) showalltasks -C : describe the corpse structure 1180 Usage: (lldb) showalltasks -R : describe the task role app status 1181 """ 1182 global kern 1183 extra_hdr = '' 1184 showcorpse = False 1185 showrole = False 1186 if '-C' in cmd_options: 1187 showcorpse = True 1188 extra_hdr += " " + GetKCDataSummary.header 1189 1190 if '-R' in cmd_options: 1191 showrole = True 1192 extra_hdr += " " + GetTaskRoleSummary.header 1193 1194 with O.table(GetTaskSummary.header + extra_hdr + " " + GetProcSummary.header): 1195 for t in kern.tasks: 1196 pval = GetProcFromTask(t) 1197 print(GetTaskSummary(t, showcorpse) + " " + (GetTaskRoleSummary(t) + " " if showrole else "") 1198 + GetProcSummary(pval)) 1199 1200 ZombTasks() 1201 1202def TaskForPmapHelper(pmap) -> Optional[value]: 1203 """ Given a pmap pointer, return the task pointer which contains that 1204 address space. 1205 1206 pmap: PMAP pointer whose task to find. 1207 """ 1208 for tasklist in [kern.tasks, kern.terminated_tasks]: 1209 for task in tasklist: 1210 if kern.GetValueFromAddress(unsigned(task.map.pmap), 'pmap_t') == pmap: 1211 return task 1212 1213 return None 1214 1215@lldb_command('taskforpmap') 1216def TaskForPmap(cmd_args=None): 1217 """ Find the task whose pmap corresponds to <pmap>. 1218 Syntax: (lldb) taskforpmap <pmap> 1219 Multiple -v's can be specified for increased verbosity 1220 """ 1221 if cmd_args is None or len(cmd_args) == 0: 1222 raise ArgumentError("Too few arguments to taskforpmap.") 1223 pmap = kern.GetValueFromAddress(cmd_args[0], 'pmap_t') 1224 task = TaskForPmapHelper(pmap) 1225 1226 if task is None: 1227 print("Couldn't find task for pmap {:#x}".format(pmap)) 1228 return 1229 1230 print(GetTaskSummary.header + " " + GetProcSummary.header) 1231 pval = GetProcFromTask(task) 1232 print(GetTaskSummary(task) + " " + GetProcSummary(pval)) 1233 1234@lldb_command('showterminatedtasks') 1235def ShowTerminatedTasks(cmd_args=None): 1236 """ Routine to print a summary listing of all the terminated tasks 1237 wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" 1238 if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung 1239 io_policy -> RAGE - rapid aging of vnodes requested 1240 NORM - normal I/O explicitly requested (this is the default) 1241 PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) 1242 THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) 1243 syntax: (lldb)showallterminatedtasks 1244 """ 1245 global kern 1246 print(GetTaskSummary.header + " " + GetProcSummary.header) 1247 for t in kern.terminated_tasks: 1248 1249 # If the task has been terminated it's likely that the process is 1250 # gone too. If there is no proc it may still be possible to find 1251 # the original proc name. 1252 pval = GetProcFromTask(t) 1253 if pval is not None: 1254 psummary = GetProcSummary(pval) 1255 else: 1256 name = GetProcNameForTask(t); 1257 pslen = GetProcSummary.header.find("command"); 1258 psummary = "{0: <{indent}} {1: <s}".format("", name, indent = pslen - 1) 1259 1260 print(GetTaskSummary(t) + " " + psummary) 1261 1262 return True 1263 1264# Macro: showtaskstacks 1265 1266def TokenExistsInStack(thread, regex): 1267 thread_val = GetLLDBThreadForKernelThread(thread) 1268 for frame in thread_val.frames: 1269 if frame.GetFunction(): 1270 func_name = frame.GetFunctionName() 1271 func_arguments = frame.arguments 1272 matching_argument = any( 1273 True if regex.search(str(arg.type)) or regex.search(str(arg.value)) else False 1274 for arg in func_arguments 1275 ) 1276 if regex.search(func_name) or matching_argument: 1277 return True 1278 return False 1279 1280def ShowTaskStacks(task, O=None, regex=None): 1281 """ Print a task with summary and stack information for each of its threads 1282 """ 1283 global kern 1284 first = True 1285 1286 for th in IterateQueue(task.threads, 'thread *', 'task_threads'): 1287 if regex is None or TokenExistsInStack(th, regex): 1288 if first: 1289 print(GetTaskSummary.header + " " + GetProcSummary.header) 1290 pval = GetProcFromTask(task) 1291 print(GetTaskSummary(task) + " " + GetProcSummary(pval)) 1292 first = False 1293 with O.table(GetThreadSummary.header, indent=True): 1294 print(GetThreadSummary(th, O=O)) 1295 print(GetThreadBackTrace(th, prefix=" ") + "\n") 1296 1297 1298def FindTasksByName(searchstr, ignore_case=True): 1299 """ Search the list of tasks by name. 1300 params: 1301 searchstr: str - a regex like string to search for task 1302 ignore_case: bool - If False then exact matching will be enforced 1303 returns: 1304 [] - array of task object. Empty if not found any 1305 """ 1306 re_options = 0 1307 if ignore_case: 1308 re_options = re.IGNORECASE 1309 search_regex = re.compile(searchstr, re_options) 1310 retval = [] 1311 for t in kern.tasks: 1312 pval = GetProcFromTask(t) 1313 process_name = "{:s}".format(GetProcName(pval)) 1314 if search_regex.search(process_name): 1315 retval.append(t) 1316 return retval 1317 1318@lldb_command('showtaskstacks', 'F:', fancy=True) 1319def ShowTaskStacksCmdHelper(cmd_args=None, cmd_options={}, O=None): 1320 """ Routine to print out the stack for each thread in a task 1321 Usage: showtaskstacks <0xaddress of task> 1322 or: showtaskstacks -F launchd 1323 """ 1324 1325 if "-F" in cmd_options: 1326 find_task_str = cmd_options["-F"] 1327 task_list = FindTasksByName(find_task_str) 1328 for tval in task_list: 1329 ShowTaskStacks(tval, O=O) 1330 return 1331 1332 if cmd_args is None or len(cmd_args) == 0: 1333 raise ArgumentError("No arguments passed") 1334 1335 tval = kern.GetValueFromAddress(cmd_args[0], 'task *') 1336 if not tval: 1337 raise ArgumentError("unknown arguments: {:s}".format(str(cmd_args))) 1338 else: 1339 ShowTaskStacks(tval, O=O) 1340 1341# EndMacro: showtaskstacks 1342 1343def CheckTaskProcRefs(task, proc, O=None): 1344 btlib = kmemory.BTLibrary.get_shared() 1345 1346 for thread in IterateQueue(task.threads, 'thread *', 'task_threads'): 1347 uthread = GetBSDThread(thread) 1348 refcount = int(uthread.uu_proc_refcount) 1349 uu_ref_info = uthread.uu_proc_ref_info 1350 if int(uu_ref_info) == 0: 1351 continue 1352 uu_ref_index = int(uu_ref_info.upri_pindex) 1353 if refcount == 0: 1354 continue 1355 for ref in range(0, uu_ref_index): 1356 if unsigned(uu_ref_info.upri_proc_ps[ref]) == unsigned(proc): 1357 print(GetTaskSummary.header + " " + GetProcSummary.header) 1358 pval = GetProcFromTask(task) 1359 print(GetTaskSummary(task) + " " + GetProcSummary(pval)) 1360 with O.table(GetThreadSummary.header, indent=True): 1361 print(GetThreadSummary(thread, O=O)) 1362 1363 bts = btlib.get_stack(unsigned(uu_ref_info.upri_proc_stacks[ref])) 1364 print(*bts.symbolicated_frames(), sep="\n") 1365 1366@lldb_command('showprocrefs', fancy=True) 1367def ShowProcRefs(cmd_args=None, cmd_options={}, O=None): 1368 """ Display information on threads/BTs that could be holding a reference on the specified proc 1369 NOTE: We can't say affirmatively if any of these references are still held since 1370 there's no way to pair references with drop-refs in the current infrastructure. 1371 Usage: showprocrefs <proc> 1372 """ 1373 if cmd_args is None or len(cmd_args) == 0: 1374 raise ArgumentError("No arguments passed") 1375 1376 proc = kern.GetValueFromAddress(cmd_args[0], 'proc *') 1377 1378 for t in kern.tasks: 1379 CheckTaskProcRefs(t, proc, O=O) 1380 for t in kern.terminated_tasks: 1381 CheckTaskProcRefs(t, proc, O=O) 1382 1383@lldb_command('showallthreads', fancy=True) 1384def ShowAllThreads(cmd_args=None, cmd_options={}, O=None): 1385 """ Display info about all threads in the system 1386 """ 1387 1388 # Terminated threads get prefixed with a 'T' 1389 def ShowTaskTerminatedThreads(task, O=O): 1390 tlist = tmap.get(unsigned(task), []) 1391 for thval in tlist: 1392 print("T\t" + GetThreadSummary(thval, O=O)) 1393 1394 # Task -> [thread, ..] map of terminated threads 1395 tmap = defaultdict(list) 1396 for thr in kern.terminated_threads: 1397 tmap[unsigned(thr.t_tro.tro_task)].append(thr) 1398 1399 for t in kern.tasks: 1400 ShowTaskThreads([str(int(t))], O=O) 1401 ShowTaskTerminatedThreads(t, O=O) 1402 print(" \n") 1403 1404 for t in kern.terminated_tasks: 1405 print("Terminated: \n") 1406 ShowTaskThreads([str(int(t))], O=O) 1407 ShowTaskTerminatedThreads(t, O=O) 1408 print(" \n") 1409 1410 return 1411 1412@lldb_command('showterminatedthreads', fancy=True) 1413def ShowTerminatedThreads(cmd_args=None, cmd_options={}, O=None): 1414 """ Display info about all terminated threads in the system 1415 """ 1416 1417 with O.table(GetThreadSummary.header, indent=True): 1418 for t in kern.terminated_threads: 1419 print(GetThreadSummary(t, O=O)) 1420 1421 1422@lldb_command('showtaskthreads', "F:", fancy=True) 1423def ShowTaskThreads(cmd_args = None, cmd_options={}, O=None): 1424 """ List the threads of a task. 1425 Usage: showtaskthreads <task-ptr> 1426 or: showtaskthreads -F <name> 1427 """ 1428 task_list = [] 1429 1430 if "-F" in cmd_options: 1431 task_list = FindTasksByName(cmd_options["-F"]) 1432 elif cmd_args: 1433 task_addr = ArgumentStringToInt(cmd_args[0]) 1434 t = addressof(kern.CreateValueFromAddress(task_addr, 'task')) 1435 task_list = [t] 1436 else: 1437 raise ArgumentError("No arguments passed") 1438 1439 for task in task_list: 1440 print(GetTaskSummary.header + " " + GetProcSummary.header) 1441 pval = GetProcFromTask(task) 1442 print(GetTaskSummary(task) + " " + GetProcSummary(pval)) 1443 with O.table(GetThreadSummary.header, indent=True): 1444 for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): 1445 print(GetThreadSummary(thval, O=O)) 1446 return 1447 1448@lldb_command('showact', fancy=True) 1449def ShowAct(cmd_args=None, cmd_options={}, O=None): 1450 """ Routine to print out the state of a specific thread. 1451 usage: showact <activation> 1452 """ 1453 if cmd_args is None or len(cmd_args) == 0: 1454 raise ArgumentError("No arguments passed") 1455 threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') 1456 with O.table(GetThreadSummary.header): 1457 print(GetThreadSummary(threadval, O=O)) 1458 1459@lldb_command('showactstack', fancy=True) 1460def ShowActStack(cmd_args=None, cmd_options={}, O=None): 1461 """ Routine to print out the stack of a specific thread. 1462 usage: showactstack <activation> 1463 """ 1464 if cmd_args is None or len(cmd_args) == 0: 1465 raise ArgumentError("No arguments passed") 1466 threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') 1467 with O.table(GetThreadSummary.header): 1468 print(GetThreadSummary(threadval, O=O)) 1469 print(GetThreadBackTrace(threadval, prefix="\t")) 1470 return 1471 1472@lldb_command('switchtoact', fancy=True) 1473def SwitchToAct(cmd_args=None, cmd_options={}, O=None): 1474 """ Switch to different context specified by activation 1475 This command allows gdb to examine the execution context and call 1476 stack for the specified activation. For example, to view the backtrace 1477 for an activation issue "switchtoact <address>", followed by "bt". 1478 Before resuming execution, issue a "resetctx" command, to 1479 return to the original execution context. 1480 """ 1481 if cmd_args is None or len(cmd_args) == 0: 1482 raise ArgumentError("No arguments passed") 1483 thval = kern.GetValueFromAddress(cmd_args[0], 'thread *') 1484 lldbthread = GetLLDBThreadForKernelThread(thval) 1485 with O.table(GetThreadSummary.header): 1486 print(GetThreadSummary(thval, O=O)) 1487 LazyTarget.GetProcess().selected_thread = lldbthread 1488 if not LazyTarget.GetProcess().SetSelectedThread(lldbthread): 1489 print("Failed to switch thread.") 1490 return 1491 1492@lldb_command('switchtoregs') 1493def SwitchToRegs(cmd_args=None): 1494 """ Routine to switch to a register state. 1495 Usage: (lldb) switchtoregs <struct arm_saved_state[64] *> 1496 This command creates a fake thread in lldb with the saved register state. 1497 Note: This command ONLY works for ARM based kernel setup. 1498 """ 1499 1500 if cmd_args is None or len(cmd_args) == 0: 1501 raise ArgumentError("No arguments passed") 1502 1503 lldb_process = LazyTarget.GetProcess() 1504 1505 saved_state = ArgumentStringToInt(cmd_args[0]) 1506 # any change to this logic requires change in operating_system.py as well 1507 fake_thread_id = 0xdead0000 | (saved_state & ~0xffff0000) 1508 fake_thread_id = fake_thread_id & 0xdeadffff 1509 lldb_process.CreateOSPluginThread(0xdeadbeef, saved_state) 1510 lldbthread = lldb_process.GetThreadByID(int(fake_thread_id)) 1511 1512 if not lldbthread.IsValid(): 1513 print("Failed to create thread") 1514 return 1515 1516 lldb_process.selected_thread = lldbthread 1517 if not lldb_process.SetSelectedThread(lldbthread): 1518 print("Failed to switch thread") 1519 print("Switched to Fake thread created from register state at {:#x}".format( 1520 saved_state)) 1521 1522# Macro: showcallchains 1523CallChainNode = namedtuple("CallChainNode", "callers threads") 1524 1525def GatherCallChainsDFS(cur_node: CallChainNode, call_chains, cur_path): 1526 if cur_node.threads: 1527 call_chain = " <- ".join(cur_path) 1528 call_chains[call_chain] = cur_node.threads 1529 1530 for next_func_name, next_node in cur_node.callers.items(): 1531 cur_path.append(next_func_name) 1532 GatherCallChainsDFS(next_node, call_chains, cur_path) 1533 cur_path.pop() 1534 1535 1536def GetCallChains(filter_regex) -> dict[str, list[str]]: 1537 ## Filter threads and build call graph 1538 root = CallChainNode({"root": CallChainNode({}, [])}, []) 1539 1540 zomb_tasks = [t for proc in kern.zombprocs if (t := GetTaskFromProc(proc)) is not None] 1541 for t in kern.tasks + zomb_tasks: 1542 for th in IterateQueue(t.threads, 'thread *', 'task_threads'): 1543 thread_val = GetLLDBThreadForKernelThread(th) 1544 cur_node = root 1545 prev_func_name = "root" 1546 matched = False 1547 1548 for frame in thread_val.frames: 1549 if frame.GetFunction(): 1550 func_name = frame.GetFunctionName() 1551 func_arguments = frame.arguments 1552 matching_argument = any( 1553 True if filter_regex.match(str(arg.type)) or filter_regex.match(str(arg.value)) else False 1554 for arg in func_arguments 1555 ) 1556 if filter_regex.match(func_name) or matching_argument: 1557 matched = True 1558 1559 callers = cur_node.callers[prev_func_name].callers 1560 callers[func_name] = callers.get(func_name, CallChainNode({}, [])) 1561 1562 cur_node = cur_node.callers[prev_func_name] 1563 prev_func_name = func_name 1564 1565 if matched: 1566 cur_node.callers[prev_func_name].threads.append(th) 1567 1568 ## gather call chains 1569 call_chains: dict[str, list[str]] = {} 1570 GatherCallChainsDFS(root.callers["root"], call_chains, []) 1571 1572 return call_chains 1573 1574CallChainThreadInfo = namedtuple('CallChainThreadInfo', ['hex', 'state', 'base', 'pri', 'since_off', 'wait_evt', 'wait_evt_sym', 'thread_name', 'task_name']) 1575 1576@lldb_command('showcallchains', fancy=True) 1577def ShowCallChains(cmd_args=None, cmd_options={}, O=None): 1578 """Routine to print out thread IDs, bucketized by function call chains 1579 1580 Usage: showcallchains <regex> 1581 The regex filters function names. Function names that don't match the regex are ignored. 1582 """ 1583 1584 if cmd_args is None or len(cmd_args) == 0: 1585 raise ArgumentError("No arguments passed.") 1586 1587 show_call_chain(cmd_args[0],O) 1588 1589def show_call_chain(param, O=None): 1590 1591 try: 1592 regex = re.compile(param) 1593 except: 1594 raise ArgumentError("Invalid predicate regex passed: {}".format(param[0])) 1595 1596 call_chains = GetCallChains(regex) 1597 1598 summary_str = "{info.hex: <20s} {info.state: <8s} {info.base: <6s} {info.pri: <10s} {info.since_off: <20s} {info.wait_evt: <20s} {info.wait_evt_sym: <20s} {info.thread_name: <20.20s} {info.task_name: <20s}" 1599 header = summary_str.format(info=CallChainThreadInfo('thread', 'state', 'base', 'pri', 'since-off (us)', 'wait_evt', 'wait_evt_sym', 'thread_name', 'task_name')) 1600 1601 ## sort desc by time_since_off 1602 from scheduler import GetSchedMostRecentDispatch 1603 most_recent_dispatch = GetSchedMostRecentDispatch(False) 1604 def GetTimeSinceOff(th): 1605 last_off = th.last_run_time 1606 time_since_off_abs = unsigned(most_recent_dispatch - last_off) 1607 return time_since_off_abs 1608 1609 for call_chain, threads in call_chains.copy().items(): 1610 call_chains[call_chain] = sorted( 1611 zip(threads, (GetTimeSinceOff(th) for th in threads)), 1612 reverse=True, 1613 key=lambda a: a[1] 1614 ) 1615 1616 ## print results 1617 for call_chain, threads in sorted( 1618 list(call_chains.items()), 1619 key = lambda a: len(a[1]) 1620 ): 1621 print("{0}, {1} thread{2}".format(call_chain, len(threads), len(threads) > 1 and "s" or "")) 1622 1623 with O.table(header, indent=True): 1624 for th, time_since_off_abs in threads: 1625 thread_hex = '{:<#018x}'.format(th) 1626 base_priority = str(int(th.base_pri)) 1627 sched_priority = str(int(th.sched_pri)) 1628 1629 state = int(th.state) 1630 state_str = '' 1631 mask = 0x1 1632 while mask <= LAST_THREAD_STATE: 1633 state_str += THREAD_STATE_CHARS[int(state & mask)] 1634 mask <<= 1 1635 if int(th.inspection): 1636 state_str += 'C' 1637 1638 wait_event_str = '' 1639 wait_event_str_sym = '' 1640 if state & 0x1: # WAIT 1641 wait_event_str = '{:<#018x}'.format(unsigned(th.wait_event)) 1642 wait_event_str_sym = kern.Symbolicate(int(hex(th.wait_event), 16)) 1643 1644 time_since_off_us = "{:,}".format(kern.GetNanotimeFromAbstime(time_since_off_abs) / 1000.0) 1645 1646 uthread = GetBSDThread(th) 1647 thread_name = GetThreadNameFromBSDThread(uthread) 1648 1649 pval = GetProcFromTask(th.t_tro.tro_task) 1650 task_name = GetProcName(pval) 1651 1652 info = CallChainThreadInfo(hex=thread_hex, state=state_str, base=base_priority, pri=sched_priority, since_off=time_since_off_us, 1653 wait_evt=wait_event_str, wait_evt_sym=wait_event_str_sym, thread_name=thread_name, task_name=task_name) 1654 info_str = summary_str.format(info=info) 1655 1656 print(O.format(info_str)) 1657 print("") 1658 1659 print("Summary:") 1660 print("{} different call chains".format(len(call_chains))) 1661# Endmacro showcallchains 1662 1663 1664# Macro: showallstacks 1665@lldb_command('showallstacks', "R:", fancy=True) 1666def ShowAllStacks(cmd_args=None, cmd_options={}, O=None): 1667 """Routine to print out the stack for each thread in the system. 1668 Usage: showallstacks [-R REGEX] 1669 -R : Only show stacks with function names matching the provided regular expression 1670 """ 1671 if "-R" in cmd_options: 1672 regex = re.compile(cmd_options['-R']) 1673 else: 1674 regex = None 1675 for t in kern.tasks: 1676 ShowTaskStacks(t, O=O, regex=regex) 1677 if regex is None: 1678 print(" \n") 1679 1680 ShowZombStacks(O=O, regex=regex) 1681# EndMacro: showallstacks 1682 1683# Macro: showcurrentstacks 1684@lldb_command('showcurrentstacks', fancy=True) 1685def ShowCurrentStacks(cmd_args=None, cmd_options={}, O=None): 1686 """ Routine to print out the thread running on each cpu (incl. its stack) 1687 """ 1688 processor_list = kern.GetGlobalVariable('processor_list') 1689 current_processor = processor_list 1690 while unsigned(current_processor) > 0: 1691 print("\n" + GetProcessorSummary(current_processor)) 1692 active_thread = current_processor.active_thread 1693 if unsigned(active_thread) != 0: 1694 task_val = active_thread.t_tro.tro_task 1695 proc_val = GetProcFromTask(task_val) 1696 print(GetTaskSummary.header + " " + GetProcSummary.header) 1697 print(GetTaskSummary(task_val) + " " + GetProcSummary(proc_val)) 1698 with O.table(GetThreadSummary.header, indent=True): 1699 print(GetThreadSummary(active_thread, O=O)) 1700 print("\tBacktrace:") 1701 print(GetThreadBackTrace(active_thread, prefix="\t")) 1702 current_processor = current_processor.processor_list 1703 return 1704# EndMacro: showcurrentstacks 1705 1706@lldb_command('showcurrentthreads', fancy=True) 1707def ShowCurrentThreads(cmd_args=None, cmd_options={}, O=None): 1708 """ Display info about threads running on each cpu """ 1709 processor_list = kern.GetGlobalVariable('processor_list') 1710 current_processor = processor_list 1711 while unsigned(current_processor) > 0: 1712 print(GetProcessorSummary(current_processor)) 1713 active_thread = current_processor.active_thread 1714 if unsigned(active_thread) != 0 : 1715 task_val = active_thread.t_tro.tro_task 1716 proc_val = GetProcFromTask(task_val) 1717 print(GetTaskSummary.header + " " + GetProcSummary.header) 1718 print(GetTaskSummary(task_val) + " " + GetProcSummary(proc_val)) 1719 with O.table(GetThreadSummary.header, indent=True): 1720 print(GetThreadSummary(active_thread, O=O)) 1721 current_processor = current_processor.processor_list 1722 return 1723 1724def GetFullBackTrace(frame_addr, verbosity = vHUMAN, prefix = ""): 1725 """ Get backtrace across interrupt context. 1726 params: frame_addr - int - address in memory which is a frame pointer (ie. rbp, r7) 1727 prefix - str - prefix for each line of output. 1728 1729 """ 1730 out_string = "" 1731 bt_count = 0 1732 frame_ptr = frame_addr 1733 previous_frame_ptr = 0 1734 while frame_ptr and frame_ptr != previous_frame_ptr and bt_count < 128: 1735 pc_val = kern.GetValueFromAddress(frame_ptr + kern.ptrsize,'uintptr_t *') 1736 pc_val = kern.StripKernelPAC(unsigned(dereference(pc_val))) 1737 out_string += prefix + GetSourceInformationForAddress(pc_val) + "\n" 1738 bt_count +=1 1739 previous_frame_ptr = frame_ptr 1740 frame_val = kern.GetValueFromAddress((frame_ptr), 'uintptr_t *') 1741 if unsigned(frame_val) == 0: 1742 break 1743 frame_ptr = unsigned(dereference(frame_val)) 1744 1745 return out_string 1746 1747@lldb_command('fullbt') 1748def FullBackTrace(cmd_args=[]): 1749 """ Show full backtrace across the interrupt boundary. 1750 Syntax: fullbt <frame ptr> 1751 Example: fullbt `$rbp` 1752 """ 1753 if len(cmd_args) < 1: 1754 raise ArgumentError() 1755 1756 print(GetFullBackTrace(ArgumentStringToInt(cmd_args[0]), prefix="\t")) 1757 1758@lldb_command('fullbtall', fancy=True) 1759def FullBackTraceAll(cmd_args=[], cmd_options={}, O=None): 1760 """ Show full backtrace across the interrupt boundary for threads running on all processors. 1761 Syntax: fullbtall 1762 Example: fullbtall 1763 """ 1764 for processor in IterateLinkedList(kern.globals.processor_list, 'processor_list') : 1765 print("\n" + GetProcessorSummary(processor)) 1766 active_thread = processor.active_thread 1767 if unsigned(active_thread) != 0 : 1768 task_val = active_thread.t_tro.tro_task 1769 proc_val = GetProcFromTask(task_val) 1770 print(GetTaskSummary.header + " " + GetProcSummary.header) 1771 print(GetTaskSummary(task_val) + " " + GetProcSummary(proc_val)) 1772 with O.table(GetThreadSummary.header, indent=True): 1773 print(GetThreadSummary(active_thread, O=O)) 1774 print("\tBacktrace:") 1775 1776 ThreadVal = GetLLDBThreadForKernelThread(active_thread) 1777 1778 FramePtr = ThreadVal.frames[0].GetFP() 1779 1780 print(GetFullBackTrace(unsigned(FramePtr), prefix="\t")) 1781 1782 1783@lldb_command('symbolicate') 1784def SymbolicateAddress(cmd_args=[]): 1785 """ Symbolicate an address for symbol information from loaded symbols 1786 Example: "symbolicate 0xaddr" is equivalent to "output/a 0xaddr" 1787 """ 1788 if len(cmd_args) < 1: 1789 print("Invalid address.\nSyntax: symbolicate <address>") 1790 return False 1791 print(GetSourceInformationForAddress(ArgumentStringToInt(cmd_args[0]))) 1792 return True 1793 1794@lldb_command('showinitchild') 1795def ShowInitChild(cmd_args=None): 1796 """ Routine to print out all processes in the system 1797 which are children of init process 1798 """ 1799 headp = kern.globals.initproc.p_children 1800 for pp in IterateListEntry(headp, 'p_sibling'): 1801 print(GetProcInfo(pp)) 1802 return 1803 1804@lldb_command('showproctree') 1805def ShowProcTree(cmd_args=None): 1806 """ Routine to print the processes in the system in a hierarchical tree form. This routine does not print zombie processes. 1807 If no argument is given, showproctree will print all the processes in the system. 1808 If pid is specified, showproctree prints all the descendants of the indicated process 1809 """ 1810 search_pid = 0 1811 if cmd_args: 1812 search_pid = ArgumentStringToInt(cmd_args[0]) 1813 1814 if search_pid < 0: 1815 raise ArgumentError("pid specified must be a positive number") 1816 1817 hdr_format = "{0: <6s} {1: <14s} {2: <9s}\n" 1818 out_string = hdr_format.format("PID", "PROCESS", "POINTER") 1819 out_string += hdr_format.format('='*3, '='*7, '='*7) 1820 proc = GetProcForPid(search_pid) 1821 out_string += "{0: <6d} {1: <32s} [ {2: #019x} ]\n".format( 1822 proc.p_ppid, GetProcName(proc.p_pptr), unsigned(proc.p_pptr)) 1823 out_string += "|--{0: <6d} {1: <32s} [ {2: #019x} ]\n".format( 1824 GetProcPID(proc), GetProcName(proc), unsigned(proc)) 1825 print(out_string) 1826 ShowProcTreeRecurse(proc, "| ") 1827 1828def ShowProcTreeRecurse(proc, prefix=""): 1829 """ Prints descendants of a given proc in hierarchial tree form 1830 params: 1831 proc : core.value representing a struct proc * in the kernel 1832 returns: 1833 str : String containing info about a given proc and its descendants in tree form 1834 """ 1835 if proc.p_childrencnt > 0: 1836 head_ptr = proc.p_children.lh_first 1837 1838 for p in IterateListEntry(proc.p_children, 'p_sibling'): 1839 print(prefix + "|--{0: <6d} {1: <32s} [ {2: #019x} ]\n".format( 1840 GetProcPID(p), GetProcName(p), unsigned(p))) 1841 ShowProcTreeRecurse(p, prefix + "| ") 1842 1843@lldb_command('showthreadfortid', fancy=True) 1844def ShowThreadForTid(cmd_args=None, O=None): 1845 """ The thread structure contains a unique thread_id value for each thread. 1846 This command is used to retrieve the address of the thread structure(thread_t) 1847 corresponding to a given thread_id. 1848 """ 1849 if cmd_args is None or len(cmd_args) == 0: 1850 raise ArgumentError("Please provide thread_t whose tid you'd like to look up") 1851 1852 search_tid = ArgumentStringToInt(cmd_args[0]) 1853 for taskp in kern.tasks: 1854 for actp in IterateQueue(taskp.threads, 'struct thread *', 'task_threads'): 1855 if search_tid == int(actp.thread_id): 1856 print("Found {0: #019x}".format(actp)) 1857 with O.table(GetThreadSummary.header): 1858 print(GetThreadSummary(actp, O=O)) 1859 return 1860 print("Not a valid thread_id") 1861 1862def GetProcessorSummary(processor): 1863 """ Internal function to print summary of processor 1864 params: processor - value representing struct processor * 1865 return: str - representing the details of given processor 1866 """ 1867 1868 processor_state_str = "INVALID" 1869 processor_state = int(processor.state) 1870 1871 processor_states = { 1872 0: 'OFF_LINE', 1873 1: 'SHUTDOWN', 1874 2: 'START', 1875 3: 'PENDING_OFFLINE', 1876 4: 'IDLE', 1877 5: 'DISPATCHING', 1878 6: 'RUNNING' 1879 } 1880 1881 if processor_state in processor_states: 1882 processor_state_str = "{0: <11s} ".format(processor_states[processor_state]) 1883 1884 processor_recommended_str = "" 1885 if int(processor.is_recommended) == 0: 1886 processor_recommended_str = " (not recommended)" 1887 1888 ast = 0 1889 preemption_disable = 0 1890 preemption_disable_str = "" 1891 1892 if kern.arch == 'x86_64': 1893 cpu_data = kern.globals.cpu_data_ptr[processor.cpu_id] 1894 if (cpu_data != 0) : 1895 ast = cpu_data.cpu_pending_ast 1896 preemption_disable = cpu_data.cpu_preemption_level 1897 # On arm64, it's kern.globals.CpuDataEntries[processor.cpu_id].cpu_data_vaddr 1898 # but LLDB can't find CpuDataEntries... 1899 1900 ast_str = GetASTSummary(ast) 1901 1902 if (preemption_disable != 0) : 1903 preemption_disable_str = "Preemption Disabled" 1904 1905 processor_reasons = { 1906 0: '(REASON_NONE)', 1907 1: '(REASON_SYSTEM)', 1908 2: '(REASON_USER)', 1909 3: '(REASON_CLPC_SYSTEM)', 1910 4: '(REASON_CLPC_USER)' 1911 } 1912 1913 processor_shutdown_reason_str = ""; 1914 processor_shutdown_reason = int(processor.last_shutdown_reason) 1915 1916 if processor_state in {0, 1, 3}: 1917 processor_shutdown_reason_str = processor_reasons[processor_shutdown_reason] 1918 1919 out_str = "Processor {: <#018x} cpu_id {:>#4x} AST: {:<6s} State {:<s}{:<s}{:<s} {:<s}\n".format( 1920 processor, int(processor.cpu_id), ast_str, processor_state_str, processor_shutdown_reason_str, 1921 processor_recommended_str, preemption_disable_str) 1922 return out_str 1923 1924ledger_limit_infinity = (uint64_t(0x1).value << 63) - 1 1925 1926def GetLedgerEntryIndex(template, name): 1927 i = 0 1928 lt_count = template.lt_cnt 1929 lt_entries = template.lt_entries 1930 1931 while i != lt_count: 1932 if str(lt_entries[i].et_key) == name: 1933 return i 1934 i += 1 1935 return -1 1936 1937def GetLedgerEntryWithTemplate(ledger_template, ledgerp, i): 1938 """ Internal function to get internals of a ledger entry (*not* a ledger itself) 1939 params: ledger_template - value representing struct ledger_template_t for the task or thread 1940 ledgerp - value representing ledger pointer 1941 i - index in ledger 1942 return: entry - entry dictionary 1943 """ 1944 lf_refill_scheduled = 0x0400 1945 lf_tracking_max = 0x4000 1946 1947 entry = {} 1948 1949 et = ledger_template.lt_entries[i] 1950 entry["key"] = str(et.et_key) 1951 et_size = et.et_size 1952 if et_size == sizeof("struct ledger_entry_small"): 1953 les = ledgerp.l_entries[et.et_offset] 1954 entry["credit"] = unsigned(les.les_credit) 1955 entry["debit"] = 0 1956 entry["flags"] = int(les.les_flags) 1957 entry["limit"] = ledger_limit_infinity 1958 elif et_size == sizeof("struct ledger_entry"): 1959 le = cast(addressof(ledgerp.l_entries[et.et_offset]), "struct ledger_entry *") 1960 entry["credit"] = unsigned(le.le_credit) 1961 entry["debit"] = unsigned(le.le_debit) 1962 le_flags = int(le.le_flags) 1963 if (le_flags & lf_tracking_max): 1964 if hasattr(le._le._le_max, "le_interval_max"): 1965 entry["interval_max"] = unsigned(le._le._le_max.le_interval_max) 1966 entry["lifetime_max"] = unsigned(le._le._le_max.le_lifetime_max) 1967 1968 entry["limit"] = unsigned(le.le_limit) 1969 1970 if (le_flags & lf_refill_scheduled): 1971 entry["refill_period"] = unsigned(le._le.le_refill.le_refill_period) 1972 1973 if (unsigned(le.le_warn_percent) < 65535): 1974 entry["warn_percent"] = unsigned (le.le_warn_percent * 100 / 65536) 1975 entry["flags"] = le_flags 1976 entry["diag_threshold_scaled"] = int(le.le_diag_threshold_scaled) 1977 else: 1978 return None 1979 1980 entry["balance"] = entry["credit"] - entry["debit"] 1981 return entry 1982 1983def GetLedgerEntryWithName(ledger_template, ledger, name): 1984 idx = GetLedgerEntryIndex(ledger_template, name) 1985 assert(idx != -1) 1986 return GetLedgerEntryWithTemplate(ledger_template, ledger, idx) 1987 1988def FormatLedgerEntrySummary(entry, i, show_footprint_interval_max=False): 1989 """ internal function to format a ledger entry into a string 1990 params: entry - A python dictionary containing the ledger entry 1991 return: str - formatted output information of ledger entries 1992 """ 1993 out_str = '' 1994 out_str += "{: >32s} {:<2d}:".format(entry["key"], i) 1995 out_str += "{: >15d} ".format(entry["balance"]) 1996 1997 if (show_footprint_interval_max): 1998 if "interval_max" in entry: 1999 out_str += "{:12d} ".format(entry["interval_max"]) 2000 else: 2001 out_str += " - " 2002 2003 if "lifetime_max" in entry: 2004 out_str += "{:14d} ".format(entry["lifetime_max"]) 2005 else: 2006 out_str += " - " 2007 2008 out_str += "{:12d} {:12d} ".format(entry["credit"], entry["debit"]) 2009 if entry.get('limit', unsigned(ledger_limit_infinity)) != unsigned(ledger_limit_infinity): 2010 out_str += "{:12d} ".format(unsigned(entry["limit"])) 2011 else: 2012 out_str += " - " 2013 2014 if "refill_period" in entry: 2015 out_str += "{:15d} ".format(entry["refill_period"]) 2016 if entry["refill_period"] != 0: 2017 out_str += "{:9d} ".format((entry["limit"] * 100) // entry["refill_period"]) 2018 else: 2019 out_str += "XXXXX - " 2020 else: 2021 out_str += " - " 2022 out_str += " - " 2023 2024 if "warn_percent" in entry: 2025 out_str += "{:9d} ".format(entry["warn_percent"]) 2026 else: 2027 out_str += " - " 2028 2029 if "limit" in entry: 2030 if entry["balance"] > entry["limit"]: 2031 out_str += " X " 2032 else: 2033 out_str += " " 2034 else: 2035 out_str += " " 2036 2037 out_str += "{:#8x}\n".format(entry["flags"]) 2038 return out_str 2039 2040def GetLedgerEntrySummary(ledger_template, ledger, i, show_footprint_interval_max=False): 2041 """ internal function to get internals of a ledger entry (*not* a ledger itself) 2042 params: ledger_template - value representing struct ledger_template_t for the task or thread 2043 ledger - value representing ledger pointer 2044 return: str - formatted output information of ledger entries 2045 """ 2046 entry = GetLedgerEntryWithTemplate(ledger_template, ledger, i) 2047 return FormatLedgerEntrySummary(entry, i) 2048 2049 2050def GetThreadLedgers(thread_val): 2051 """ Internal function to get a summary of ledger entries for the given thread 2052 params: thread_val - value representing struct thread * 2053 return: thread - python dictionary containing threads's ledger entries. This can 2054 be printed directly with FormatThreadLedgerSummmary or outputted as json. 2055 """ 2056 thread = {} 2057 thread["address"] = unsigned(thread_val) 2058 ledgerp = thread_val.t_threadledger 2059 thread["entries"] = [] 2060 if ledgerp: 2061 i = 0 2062 while i != ledgerp.l_template.lt_cnt: 2063 thread["entries"].append(GetLedgerEntryWithTemplate(kern.globals.thread_ledger_template, 2064 ledgerp, i)) 2065 i = i + 1 2066 return thread 2067 2068def FormatThreadLedgerSummary(thread): 2069 """ Internal function to print a thread's ledger entries 2070 params: thread - python dictionary containing thread's ledger entries 2071 return: str - formatted output information for ledger entries of the input thread 2072 """ 2073 out_str = " [{:#08x}]\n".format(thread["address"]) 2074 entries = thread["entries"] 2075 for i, entry in enumerate(entries): 2076 out_str += FormatLedgerEntrySummary(entry, i) 2077 return out_str 2078 2079def GetTaskLedgers(task_val): 2080 """ Internal function to get summary of ledger entries from the task and its threads 2081 params: task_val - value representing struct task * 2082 return: task - python dictionary containing tasks's ledger entries. This can 2083 be printed directly with FormatTaskLedgerSummary or outputted as json. 2084 """ 2085 task_ledgerp = task_val.ledger 2086 i = 0 2087 tasks = [] 2088 task = {} 2089 task["address"] = unsigned(task_val) 2090 2091 pval = GetProcFromTask(task_val) 2092 if pval is not None: 2093 task["name"] = GetProcName(pval) 2094 task["pid"] = int(GetProcPID(pval)) 2095 2096 task["entries"] = [] 2097 while i != task_ledgerp.l_template.lt_cnt: 2098 task["entries"].append(GetLedgerEntryWithTemplate(kern.globals.task_ledger_template, task_ledgerp, i)) 2099 i = i + 1 2100 2101 # Now walk threads 2102 task["threads"] = [] 2103 for thval in IterateQueue(task_val.threads, 'thread *', 'task_threads'): 2104 task["threads"].append(GetThreadLedgers(thval)) 2105 2106 return task 2107 2108@header("{0: <15s} {1: >16s} {2: <2s} {3: >15s} {4: >14s} {5: >12s} {6: >12s} {7: >12s} {8: <15s} {9: <8s} {10: <9s} {11: <6s} {12: >6s}".format( 2109 "task [thread]", "entry", "#", "balance", "lifetime_max", "credit", 2110 "debit", "limit", "refill period", "lim pct", "warn pct", "over?", "flags")) 2111def FormatTaskLedgerSummary(task, show_footprint_interval_max=False): 2112 """ Internal function to get summary of ledger entries from the task and its threads 2113 params: task_val - value representing struct task * 2114 return: str - formatted output information for ledger entries of the input task 2115 """ 2116 out_str = '' 2117 out_str += "{: #08x} ".format(task["address"]) 2118 if "name" in task: 2119 out_str += "{: <5s}:\n".format(task["name"]) 2120 else: 2121 out_str += "Invalid process\n" 2122 2123 for i, entry in enumerate(task["entries"]): 2124 out_str += FormatLedgerEntrySummary(entry, i, show_footprint_interval_max) 2125 2126 for thread in task["threads"]: 2127 out_str += FormatThreadLedgerSummary(thread) 2128 return out_str 2129 2130 2131# Macro: showtaskledgers 2132 2133@lldb_command('showtaskledgers', 'JF:I') 2134def ShowTaskLedgers(cmd_args=None, cmd_options={}): 2135 """ Routine to print a summary of ledger entries for the task and all of its threads 2136 or : showtaskledgers [ -I ] [-J] [ -F ] <task> 2137 options: 2138 -I: show footprint interval max (DEV/DEBUG only) 2139 -F: specify task via name instead of address 2140 -J: output json 2141 - 2142 """ 2143 print_json = False 2144 if "-F" in cmd_options: 2145 task_list = FindTasksByName(cmd_options["-F"]) 2146 for tval in task_list: 2147 print(FormatTaskLedgerSummary.header) 2148 ledgers = GetTaskLedgers(tval) 2149 print(FormatTaskLedgerSummary(ledgers)) 2150 return 2151 if "-J" in cmd_options: 2152 print_json = True 2153 2154 if cmd_args is None or len(cmd_args) == 0: 2155 raise ArgumentError("No arguments passed.") 2156 show_footprint_interval_max = False 2157 if "-I" in cmd_options: 2158 show_footprint_interval_max = True 2159 tval = addressof(kern.CreateValueFromAddress(cmd_args[0], 'task')) 2160 if not tval: 2161 raise ArgumentError("unknown arguments: %r" %cmd_args) 2162 ledgers = GetTaskLedgers(tval) 2163 if print_json: 2164 print(json.dumps(ledgers)) 2165 else: 2166 if (show_footprint_interval_max): 2167 print("{0: <15s} {1: >16s} {2: <2s} {3: >15s} {4: >12s} {5: >14s} {6: >12s} {7: >12s} {8: >12s} {9: <15s} {10: <8s} {11: <9s} {12: <6s} {13: >6s}".format( 2168 "task [thread]", "entry", "#", "balance", "intrvl_max", "lifetime_max", "credit", 2169 "debit", "limit", "refill period", "lim pct", "warn pct", "over?", "flags")) 2170 else: 2171 print(FormatTaskLedgerSummary.header) 2172 print(FormatTaskLedgerSummary(ledgers, show_footprint_interval_max)) 2173 2174# EndMacro: showtaskledgers 2175 2176# Macro: showalltaskledgers 2177 2178@lldb_command('showalltaskledgers', "J") 2179def ShowAllTaskLedgers(cmd_args=None, cmd_options={}): 2180 """ Routine to print a summary of ledger entries for all tasks and respective threads 2181 Usage: showalltaskledgers [-J] 2182 -J : Output json 2183 """ 2184 print_json = False 2185 if "-J" in cmd_options: 2186 print_json = True 2187 tasks = [] 2188 for t in kern.tasks: 2189 task_val = unsigned(t) 2190 if not print_json: 2191 ShowTaskLedgers([task_val], cmd_options=cmd_options) 2192 else: 2193 tasks.append(GetTaskLedgers(t)) 2194 if print_json: 2195 print(json.dumps(tasks)) 2196 2197# EndMacro: showalltaskledgers 2198 2199# Macro: showprocuuidpolicytable 2200 2201@lldb_type_summary(['proc_uuid_policy_entry']) 2202@header("{0: <36s} {1: <10s}".format("uuid", "flags")) 2203def GetProcUUIDPolicyEntrySummary(entry): 2204 """ Summarizes the important fields in proc_uuid_policy_entry structure. 2205 params: entry: value - value object representing an entry 2206 returns: str - summary of the entry 2207 """ 2208 data = [] 2209 for i in range(16): 2210 data.append(int(entry.uuid[i])) 2211 flags = unsigned(entry.flags) 2212 out_string = "{a[0]:02X}{a[1]:02X}{a[2]:02X}{a[3]:02X}-{a[4]:02X}{a[5]:02X}-{a[6]:02X}{a[7]:02X}-{a[8]:02X}{a[9]:02X}-{a[10]:02X}{a[11]:02X}{a[12]:02X}{a[13]:02X}{a[14]:02X}{a[15]:02X} 0x{b:0>8x}".format(a=data, b=flags) 2213 return out_string 2214 2215@lldb_command('showprocuuidpolicytable') 2216def ShowProcUUIDPolicyTable(cmd_args=None): 2217 """ Routine to print the proc UUID policy table 2218 Usage: showprocuuidpolicytable 2219 """ 2220 hashslots = unsigned(kern.globals.proc_uuid_policy_hash_mask) 2221 print("{0: <8s} ".format("slot") + GetProcUUIDPolicyEntrySummary.header) 2222 for i in range(0, hashslots+1): 2223 headp = addressof(kern.globals.proc_uuid_policy_hashtbl[i]) 2224 entrynum = 0 2225 for entry in IterateListEntry(headp, 'entries'): 2226 print("{0: >2d}.{1: <5d} ".format(i, entrynum) + GetProcUUIDPolicyEntrySummary(entry)) 2227 entrynum += 1 2228 2229 2230# EndMacro: showprocuuidpolicytable 2231def build_fields_string(obj: value, fields: list[list[str]]) -> str: 2232 result = "" 2233 for field_name, human_name in fields: 2234 if (trp_field_value := get_field(obj, field_name)) is not None: 2235 result += f"{human_name}: {str(trp_field_value)} " 2236 2237 return result 2238 2239@lldb_command('showalltaskpolicy') 2240def ShowAllTaskPolicy(cmd_args=None): 2241 """ 2242 Routine to print a summary listing of all the tasks 2243 wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" 2244 if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung 2245 io_policy -> RAGE - rapid aging of vnodes requested 2246 NORM - normal I/O explicitly requested (this is the default) 2247 PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) 2248 THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) 2249 """ 2250 global kern 2251 print(GetTaskSummary.header + " " + GetProcSummary.header) 2252 for t in kern.tasks: 2253 pval = GetProcFromTask(t) 2254 print(GetTaskSummary(t) +" "+ GetProcSummary(pval)) 2255 2256 requested_strings = [ 2257 ["trp_int_darwinbg", "DBG-int"], 2258 ["trp_ext_darwinbg", "DBG-ext"], 2259 ["trp_int_iotier", "iotier-int"], 2260 ["trp_ext_iotier", "iotier-ext"], 2261 ["trp_int_iopassive", "passive-int"], 2262 ["trp_ext_iopassive", "passive-ext"], 2263 ["trp_bg_iotier", "bg-iotier"], 2264 ["trp_terminated", "terminated"], 2265 # ["thrp_pidbind_bg", "bg-pidbind"], # no longer part of task requested policy 2266 ["trp_apptype", "apptype"], 2267 ["trp_boosted", "boosted"], 2268 ["trp_role", "role"], 2269 # ["trp_tal_enabled", "tal-enabled"], # tal_enabled is unused/deprecated 2270 ["trp_base_latency_qos", "latency-base"], 2271 ["trp_over_latency_qos", "latency-override"], 2272 ["trp_base_through_qos", "throughput-base"], 2273 ["trp_over_through_qos", "throughput-override"] 2274 ] 2275 2276 requested = build_fields_string(t.requested_policy, requested_strings) 2277 2278 suppression_strings = [ 2279 ["trp_sup_active", "active"], 2280 ["trp_sup_lowpri_cpu", "lowpri-cpu"], 2281 ["trp_sup_timer", "timer-throttling"], 2282 ["trp_sup_disk", "disk-throttling"], 2283 ["trp_sup_cpu_limit", "cpu-limits"], 2284 ["trp_sup_suspend", "suspend"], 2285 ["trp_sup_bg_sockets", "bg-sockets"] 2286 ] 2287 suppression = build_fields_string(t.requested_policy, suppression_strings) 2288 2289 effective_strings = [ 2290 ["tep_darwinbg", "background"], 2291 ["tep_lowpri_cpu", "lowpri-cpu"], 2292 ["tep_io_tier", "iotier"], 2293 ["tep_io_passive", "passive"], 2294 ["tep_all_sockets_bg", "bg-allsockets"], 2295 ["tep_new_sockets_bg", "bg-newsockets"], 2296 ["tep_bg_iotier", "bg-iotier"], 2297 ["tep_terminated", "terminated"], 2298 # ["t_gpu_deny", "gpu-deny"], # no longer exists 2299 ["tep_tal_engaged", "tal-engaged"], 2300 # ["t_suspended", "suspended"], # no longer exists 2301 ["tep_watchers_bg", "bg-watchers"], 2302 ["tep_latency_qos", "latency-qos"], 2303 ["tep_through_qos", "throughput-qos"], 2304 ["tep_sup_active", "suppression-active"], 2305 ["tep_role", "role"] 2306 ] 2307 effective = build_fields_string(t.effective_policy, effective_strings) 2308 2309 print("requested: " + requested) 2310 print("suppression: " + suppression) 2311 print("effective: " + effective) 2312 print("\n\n") 2313 2314 2315@lldb_command('showallsuspendedtasks', '') 2316def ShowSuspendedTasks(cmd_args=[], options={}): 2317 """ Show a list of suspended tasks with their process name summary. 2318 """ 2319 print(GetTaskSummary.header + ' ' + GetProcSummary.header) 2320 for t in kern.tasks: 2321 if t.suspend_count > 0: 2322 print(GetTaskSummary(t) + ' ' + GetProcSummary(GetProcFromTask(t))) 2323 return True 2324 2325# Macro: showallpte 2326@lldb_command('showallpte') 2327def ShowAllPte(cmd_args=None): 2328 """ Prints out the physical address of the pte for all tasks 2329 """ 2330 head_taskp = addressof(kern.globals.tasks) 2331 taskp = Cast(head_taskp.next, 'task *') 2332 while taskp != head_taskp: 2333 out_str = "task = {:#x} pte = {:#x}\t".format(taskp, taskp.map.pmap.ttep) 2334 procp = GetProcFromTask(taskp) 2335 if procp is not None: 2336 out_str += "{:s}\n".format(GetProcName(procp)) 2337 else: 2338 out_str += "\n" 2339 print(out_str) 2340 taskp = Cast(taskp.tasks.next, 'struct task *') 2341 2342# EndMacro: showallpte 2343 2344# Macro: showallrefcounts 2345@lldb_command('showallrefcounts') 2346@header("{0: <20s} {1: ^10s}".format("task", "ref_count")) 2347def ShowAllRefCounts(cmd_args=None): 2348 """ Prints the ref_count of all tasks 2349 """ 2350 out_str = '' 2351 head_taskp = addressof(kern.globals.tasks) 2352 taskp = Cast(head_taskp.next, 'task *') 2353 print(ShowAllRefCounts.header) 2354 while taskp != head_taskp: 2355 out_str += "{: <#20x}".format(taskp) 2356 out_str += "{: ^10d}\n".format(taskp.ref_count.ref_count) 2357 taskp = Cast(taskp.tasks.next, 'task *') 2358 print(out_str) 2359# EndMacro: showallrefcounts 2360 2361# Macro: showallrunnablethreads 2362@lldb_command('showallrunnablethreads', fancy=True) 2363def ShowAllRunnableThreads(cmd_args=None, cmd_options={}, O=None): 2364 """ Prints the sched usage information for all threads of each task 2365 """ 2366 out_str = '' 2367 for taskp in kern.tasks: 2368 for actp in IterateQueue(taskp.threads, 'thread *', 'task_threads'): 2369 if int(actp.state & 0x4): 2370 ShowActStack([unsigned(actp)], O=O) 2371 2372# EndMacro: showallrunnablethreads 2373 2374# Macro: showallschedusage 2375@lldb_command('showallschedusage') 2376@header("{0:<20s} {1:^10s} {2:^10s} {3:^15s}".format("Thread", "Priority", "State", "sched_usage")) 2377def ShowAllSchedUsage(cmd_args=None): 2378 """ Prints the sched usage information for all threads of each task 2379 """ 2380 out_str = '' 2381 for taskp in kern.tasks: 2382 ShowTask([str(unsigned(taskp))]) 2383 print(ShowAllSchedUsage.header) 2384 for actp in IterateQueue(taskp.threads, 'thread *', 'task_threads'): 2385 out_str = "{: <#20x}".format(actp) 2386 out_str += "{: ^10s}".format(str(int(actp.sched_pri))) 2387 state = int(actp.state) 2388 state_str = '' 2389 mask = 0x1 2390 while mask <= LAST_THREAD_STATE: 2391 state_str += THREAD_STATE_CHARS[int(state & mask)] 2392 mask <<= 1 2393 out_str += "{: ^10s}".format(state_str) 2394 out_str += "{: >15d}".format(actp.sched_usage) 2395 print(out_str + "\n") 2396 print("\n\n") 2397 2398# EndMacro: showallschedusage 2399 2400#Macro: showprocfilessummary 2401@lldb_command('showprocfilessummary') 2402@header("{0: <20s} {1: <20s} {2: >10s}".format("Process", "Name", "Number of Open Files")) 2403def ShowProcFilesSummary(cmd_args=None): 2404 """ Display the summary of open file descriptors for all processes in task list 2405 Usage: showprocfilessummary 2406 """ 2407 print(ShowProcFilesSummary.header) 2408 for proc in kern.procs: 2409 proc_filedesc = addressof(proc.p_fd) 2410 proc_ofiles = proc_filedesc.fd_ofiles 2411 proc_file_count = 0 2412 for fd in range(0, proc_filedesc.fd_afterlast): 2413 if unsigned(proc_ofiles[fd]) != 0: 2414 proc_file_count += 1 2415 print("{0: <#020x} {1: <32s} {2: >10d}".format(proc, GetProcName(proc), proc_file_count)) 2416 2417#EndMacro: showprocfilessummary 2418 2419@lldb_command('workinguserstacks') 2420def WorkingUserStacks(cmd_args=None): 2421 """ Print out the user stack for each thread in a task, followed by the user libraries. 2422 Syntax: (lldb) workinguserstacks <task_t> 2423 """ 2424 if cmd_args is None or len(cmd_args) == 0: 2425 raise ArgumentError() 2426 2427 task = kern.GetValueFromAddress(cmd_args[0], 'task *') 2428 print(GetTaskSummary.header + " " + GetProcSummary.header) 2429 pval = GetProcFromTask(task) 2430 print(GetTaskSummary(task) + " " + GetProcSummary(pval) + "\n \n") 2431 for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): 2432 print("For thread 0x{0:x}".format(thval)) 2433 try: 2434 ShowThreadUserStack([hex(thval)]) 2435 except Exception as exc_err: 2436 print("Failed to show user stack for thread 0x{0:x}".format(thval)) 2437 if config['debug']: 2438 raise exc_err 2439 else: 2440 print("Enable debugging ('(lldb) xnudebug debug') to see detailed trace.") 2441 WorkingUserLibraries([hex(task)]) 2442 2443@static_var("exec_load_path", 0) 2444@lldb_command("workingkuserlibraries") 2445def WorkingUserLibraries(cmd_args=None): 2446 """ Show binary images known by dyld in target task 2447 For a given user task, inspect the dyld shared library state and print information about all Mach-O images. 2448 Syntax: (lldb)workinguserlibraries <task_t> 2449 """ 2450 if cmd_args is None or len(cmd_args) == 0: 2451 raise ArgumentError() 2452 2453 print("{0: <18s} {1: <12s} {2: <36s} {3: <50s}".format('address','type','uuid','path')) 2454 out_format = "0x{0:0>16x} {1: <12s} {2: <36s} {3: <50s}" 2455 task = kern.GetValueFromAddress(cmd_args[0], 'task_t') 2456 is_task_64 = int(task.t_flags) & 0x1 2457 dyld_all_image_infos_address = unsigned(task.all_image_info_addr) 2458 cur_data_offset = 0 2459 if dyld_all_image_infos_address == 0: 2460 print("No dyld shared library information available for task") 2461 return False 2462 vers_info_data = GetUserDataAsString(task, dyld_all_image_infos_address, 112) 2463 version = _ExtractDataFromString(vers_info_data, cur_data_offset, "uint32_t") 2464 cur_data_offset += 4 2465 if version > 12: 2466 print("Unknown dyld all_image_infos version number %d" % version) 2467 image_info_count = _ExtractDataFromString(vers_info_data, cur_data_offset, "uint32_t") 2468 WorkingUserLibraries.exec_load_path = 0 2469 if is_task_64: 2470 image_info_size = 24 2471 image_info_array_address = _ExtractDataFromString(vers_info_data, 8, "uint64_t") 2472 dyld_load_address = _ExtractDataFromString(vers_info_data, 8*4, "uint64_t") 2473 dyld_all_image_infos_address_from_struct = _ExtractDataFromString(vers_info_data, 8*13, "uint64_t") 2474 else: 2475 image_info_size = 12 2476 image_info_array_address = _ExtractDataFromString(vers_info_data, 4*2, "uint32_t") 2477 dyld_load_address = _ExtractDataFromString(vers_info_data, 4*5, "uint32_t") 2478 dyld_all_image_infos_address_from_struct = _ExtractDataFromString(vers_info_data, 4*14, "uint32_t") 2479 # Account for ASLR slide before dyld can fix the structure 2480 dyld_load_address = dyld_load_address + (dyld_all_image_infos_address - dyld_all_image_infos_address_from_struct) 2481 2482 i = 0 2483 while i < image_info_count: 2484 image_info_address = image_info_array_address + i * image_info_size 2485 img_data = GetUserDataAsString(task, image_info_address, image_info_size) 2486 if is_task_64: 2487 image_info_addr = _ExtractDataFromString(img_data, 0, "uint64_t") 2488 image_info_path = _ExtractDataFromString(img_data, 8, "uint64_t") 2489 else: 2490 image_info_addr = _ExtractDataFromString(img_data, 0, "uint32_t") 2491 image_info_path = _ExtractDataFromString(img_data, 4, "uint32_t") 2492 PrintImageInfo(task, image_info_addr, image_info_path) 2493 i += 1 2494 2495 # load_path might get set when the main executable is processed. 2496 if WorkingUserLibraries.exec_load_path != 0: 2497 PrintImageInfo(task, dyld_load_address, WorkingUserLibraries.exec_load_path) 2498 return 2499 2500# Macro: showstackaftertask 2501 2502@lldb_command('showstackaftertask', 'F:', fancy=True) 2503def Showstackaftertask(cmd_args=None, cmd_options={}, O=None): 2504 """ Routine to print the thread stacks for all tasks succeeding a given task 2505 Usage: showstackaftertask <0xaddress of task> 2506 or: showstackaftertask -F <taskname> 2507 """ 2508 if "-F" in cmd_options: 2509 # Find the task pointer corresponding to its task name 2510 find_task_str = cmd_options["-F"] 2511 task_list = FindTasksByName(find_task_str) 2512 2513 # Iterate through the list of tasks and print all task stacks thereafter 2514 for tval in task_list: 2515 ListTaskStacks(tval, O=O) 2516 return 2517 2518 if cmd_args is None or len(cmd_args) == 0: 2519 raise ArgumentError("Insufficient arguments") 2520 tval = kern.GetValueFromAddress(cmd_args[0], 'task *') 2521 if not tval: 2522 raise ArgumentError("unknown arguments: {:s}".format(str(cmd_args))) 2523 else: 2524 ListTaskStacks(tval, O=O) 2525 2526 ShowZombStacks(O=O) 2527 2528# EndMacro: showstackaftertask 2529 2530def ListTaskStacks(task, O=None): 2531 """ Search for a given task and print the list of all task stacks thereafter. 2532 """ 2533 # Initialize local variable task_flag to mark when a given task is found. 2534 task_flag=0 2535 2536 for t in kern.tasks: 2537 if (task_flag == 1): 2538 ShowTaskStacks(t, O=O) 2539 print("\n") 2540 if (t == task): 2541 task_flag = 1 2542 2543# Macro: showstackafterthread 2544@lldb_command('showstackafterthread', fancy=True) 2545def Showstackafterthread(cmd_args=None, cmd_options={}, O=None): 2546 """ Routine to print the stacks of all threads succeeding a given thread. 2547 Usage: Showstackafterthread <0xaddress of thread> 2548 """ 2549 # local variable thread_flag is used to mark when a given thread is found. 2550 thread_flag=0 2551 if cmd_args: 2552 threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') 2553 else: 2554 raise ArgumentError("No arguments passed") 2555 # Iterate through list of all tasks to look up a given thread 2556 for t in kern.tasks: 2557 if(thread_flag==1): 2558 pval = GetProcFromTask(t) 2559 print(GetTaskSummary.header + " "+ GetProcSummary.header) 2560 print(GetTaskSummary(t) + " "+ GetProcSummary(pval)) 2561 print("\n") 2562 # Look up for a given thread from the the list of threads of a given task 2563 for thval in IterateQueue(t.threads, 'thread *', 'task_threads'): 2564 if thread_flag == 1: 2565 print("\n") 2566 with O.table(GetThreadSummary.header, indent=True): 2567 print(GetThreadSummary(active_thread, O=O)) 2568 print(GetThreadBackTrace(thval, prefix="\t")+"\n") 2569 print("\n") 2570 2571 if thval == threadval: 2572 pval = GetProcFromTask(t) 2573 process_name = "{:s}".format(GetProcName(pval)) 2574 print("\n\n") 2575 print(" *** Continuing to dump the thread stacks from the process *** :" + " " + process_name) 2576 print("\n\n") 2577 thread_flag = 1 2578 print('\n') 2579 return 2580 2581