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