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 task, proc in GetAllTasks(): 923 if proc is not None and GetProcPID(proc) == pidval: 924 print(GetTaskSummary.header + " " + GetProcSummary.header) 925 print(GetTaskSummary(task) + " " + GetProcSummary(proc)) 926 break 927 928# EndMacro: showpid 929 930# Macro: showproc 931 932@lldb_command('showproc') 933def ShowProc(cmd_args=None): 934 """ Routine to print a summary listing of task corresponding to given proc 935 Usage: showproc <address of proc> 936 """ 937 if cmd_args is None or len(cmd_args) == 0: 938 raise ArgumentError("No arguments passed") 939 pval = kern.GetValueFromAddress(cmd_args[0], 'proc *') 940 if not pval: 941 print("unknown arguments:", str(cmd_args)) 942 return False 943 print(GetTaskSummary.header + " " + GetProcSummary.header) 944 tval = GetTaskFromProc(pval) 945 print(GetTaskSummary(tval) + " " + GetProcSummary(pval)) 946 947# EndMacro: showproc 948 949# Macro: showprocinfo 950 951@lldb_command('showprocinfo') 952def ShowProcInfo(cmd_args=None): 953 """ Routine to display name, pid, parent & task for the given proc address 954 It also shows the Cred, Flags and state of the process 955 Usage: showprocinfo <address of proc> 956 """ 957 if cmd_args is None or len(cmd_args) == 0: 958 raise ArgumentError("No arguments passed") 959 pval = kern.GetValueFromAddress(cmd_args[0], 'proc *') 960 if not pval: 961 print("unknown arguments:", str(cmd_args)) 962 return False 963 print(GetProcInfo(pval)) 964 965# EndMacro: showprocinfo 966 967#Macro: showprocfiles 968 969@lldb_command('showprocfiles') 970def ShowProcFiles(cmd_args=None): 971 """ Given a proc_t pointer, display the list of open file descriptors for the referenced process. 972 Usage: showprocfiles <proc_t> 973 """ 974 if cmd_args is None or len(cmd_args) == 0: 975 raise ArgumentError() 976 977 proc = kern.GetValueFromAddress(cmd_args[0], 'proc_t') 978 proc_filedesc = addressof(proc.p_fd) 979 proc_ofiles = proc_filedesc.fd_ofiles 980 if unsigned(proc_ofiles) == 0: 981 print('No open files for proc {0: <s}'.format(cmd_args[0])) 982 return 983 print("{0: <5s} {1: <18s} {2: <10s} {3: <8s} {4: <18s} {5: <64s}".format('FD', 'FILEGLOB', 'FG_FLAGS', 'FG_TYPE', 'FG_DATA','INFO')) 984 print("{0:-<5s} {0:-<18s} {0:-<10s} {0:-<8s} {0:-<18s} {0:-<64s}".format("")) 985 986 for fd in range(0, unsigned(proc_filedesc.fd_afterlast)): 987 if unsigned(proc_ofiles[fd]) != 0: 988 out_str = '' 989 proc_fd_flags = proc_ofiles[fd].fp_flags 990 proc_fd_fglob = proc_ofiles[fd].fp_glob 991 proc_fd_fglob_fg_data = Cast(proc_fd_fglob.fg_data, 'void *') 992 out_str += "{0: <5d} ".format(fd) 993 out_str += "{0: <#18x} ".format(unsigned(proc_fd_fglob)) 994 out_str += "0x{0:0>8x} ".format(unsigned(proc_fd_flags)) 995 proc_fd_ftype = unsigned(proc_fd_fglob.fg_ops.fo_type) 996 if proc_fd_ftype in xnudefines.filetype_strings: 997 out_str += "{0: <8s} ".format(xnudefines.filetype_strings[proc_fd_ftype]) 998 else: 999 out_str += "?: {0: <5d} ".format(proc_fd_ftype) 1000 out_str += "{0: <#18x} ".format(unsigned(proc_fd_fglob_fg_data)) 1001 if proc_fd_ftype == 1: 1002 fd_name = Cast(proc_fd_fglob_fg_data, 'struct vnode *').v_name 1003 out_str += "{0: <64s}".format(fd_name) 1004 out_str += "\n" 1005 print(out_str) 1006 1007#EndMacro: showprocfiles 1008 1009#Macro: showtty 1010 1011@lldb_command('showtty') 1012def ShowTTY(cmd_args=None): 1013 """ Display information about a struct tty 1014 Usage: showtty <tty struct> 1015 """ 1016 if cmd_args is None or len(cmd_args) == 0: 1017 raise ArgumentError() 1018 1019 1020 tty = kern.GetValueFromAddress(cmd_args[0], 'struct tty *') 1021 print("TTY structure at: {0: <s}".format(cmd_args[0])) 1022 print("Last input to raw queue: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_rawq.c_cs), tty.t_rawq.c_cs)) 1023 print("Last input to canonical queue: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_canq.c_cs), tty.t_canq.c_cs)) 1024 print("Last output data: {0: <#18x} \"{1: <s}\"".format(unsigned(tty.t_outq.c_cs), tty.t_outq.c_cs)) 1025 tty_state_info = [ 1026 ['', 'TS_SO_OLOWAT (Wake up when output <= low water)'], 1027 ['- (synchronous I/O mode)', 'TS_ASYNC (async I/O mode)'], 1028 ['', 'TS_BUSY (Draining output)'], 1029 ['- (Carrier is NOT present)', 'TS_CARR_ON (Carrier is present)'], 1030 ['', 'TS_FLUSH (Outq has been flushed during DMA)'], 1031 ['- (Open has NOT completed)', 'TS_ISOPEN (Open has completed)'], 1032 ['', 'TS_TBLOCK (Further input blocked)'], 1033 ['', 'TS_TIMEOUT (Wait for output char processing)'], 1034 ['', 'TS_TTSTOP (Output paused)'], 1035 ['', 'TS_WOPEN (Open in progress)'], 1036 ['', 'TS_XCLUDE (Tty requires exclusivity)'], 1037 ['', 'TS_BKSL (State for lowercase \\ work)'], 1038 ['', 'TS_CNTTB (Counting tab width, ignore FLUSHO)'], 1039 ['', 'TS_ERASE (Within a \\.../ for PRTRUB)'], 1040 ['', 'TS_LNCH (Next character is literal)'], 1041 ['', 'TS_TYPEN (Retyping suspended input (PENDIN))'], 1042 ['', 'TS_CAN_BYPASS_L_RINT (Device in "raw" mode)'], 1043 ['- (Connection NOT open)', 'TS_CONNECTED (Connection open)'], 1044 ['', 'TS_SNOOP (Device is being snooped on)'], 1045 ['', 'TS_SO_OCOMPLETE (Wake up when output completes)'], 1046 ['', 'TS_ZOMBIE (Connection lost)'], 1047 ['', 'TS_CAR_OFLOW (For MDMBUF - handle in driver)'], 1048 ['', 'TS_CTS_OFLOW (For CCTS_OFLOW - handle in driver)'], 1049 ['', 'TS_DSR_OFLOW (For CDSR_OFLOW - handle in driver)'] 1050 ] 1051 index = 0 1052 mask = 0x1 1053 tty_state = unsigned(tty.t_state) 1054 print("State:") 1055 while index < 24: 1056 if tty_state & mask != 0: 1057 if len(tty_state_info[index][1]) > 0: 1058 print('\t' + tty_state_info[index][1]) 1059 else: 1060 if len(tty_state_info[index][0]) > 0: 1061 print('\t' + tty_state_info[index][0]) 1062 index += 1 1063 mask = mask << 1 1064 print("Flags: 0x{0:0>8x}".format(unsigned(tty.t_flags))) 1065 print("Foreground Process Group: 0x{0:0>16x}".format(unsigned(tty.t_pgrp))) 1066 print("Enclosing session: 0x{0:0>16x}".format(unsigned(tty.t_session))) 1067 print("Termios:") 1068 print("\tInput Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_iflag))) 1069 print("\tOutput Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_oflag))) 1070 print("\tControl Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_cflag))) 1071 print("\tLocal Flags: 0x{0:0>8x}".format(unsigned(tty.t_termios.c_lflag))) 1072 print("\tInput Speed: {0: <8d}".format(tty.t_termios.c_ispeed)) 1073 print("\tOutput Speed: {0: <8d}".format(tty.t_termios.c_ospeed)) 1074 print("High Watermark: {0: <d} bytes".format(tty.t_hiwat)) 1075 print("Low Watermark : {0: <d} bytes".format(tty.t_lowat)) 1076 1077#EndMacro: showtty 1078 1079#Macro showallttydevs 1080 1081@lldb_command('showallttydevs') 1082def ShowAllTTYDevs(cmd_args=[], cmd_options={}): 1083 """ Show a list of ttydevs registered in the system. 1084 Usage: 1085 (lldb)showallttydevs 1086 """ 1087 tty_dev_head = kern.globals.tty_dev_head 1088 tty_dev = tty_dev_head 1089 print(GetTTYDevSummary.header) 1090 while unsigned(tty_dev) != 0: 1091 print(GetTTYDevSummary(tty_dev)) 1092 tty_dev = tty_dev.next 1093 return "" 1094 1095#EndMacro: showallttydevs 1096 1097#Macro: dumpthread_terminate_queue 1098 1099@lldb_command('dumpthread_terminate_queue', fancy=True) 1100def DumpThreadTerminateQueue(cmd_args=None, cmd_options={}, O=None): 1101 """ Displays the contents of the specified call_entry queue. 1102 Usage: dumpthread_terminate_queue 1103 """ 1104 1105 count = 0 1106 with O.table(GetThreadSummary.header): 1107 for th in IterateMPSCQueue(addressof(kern.globals.thread_terminate_queue.mpd_queue), 'struct thread', 'mpsc_links'): 1108 print(GetThreadSummary(th, O=O)) 1109 count += 1 1110 print("{0: <d} entries!".format(count)) 1111 1112#EndMacro: dumpthread_terminate_queue 1113 1114#Macro: dumpcrashed_thread_queue 1115 1116@lldb_command('dumpcrashed_thread_queue', fancy=True) 1117def DumpCrashedThreadsQueue(cmd_args=None, cmd_options={}, O=None): 1118 """ Displays the contents of the specified call_entry queue. 1119 Usage: dumpcrashed_thread_queue 1120 """ 1121 1122 count = 0 1123 with O.table(GetThreadSummary.header): 1124 for th in IterateQueue(addressof(kern.globals.crashed_threads_queue), 'struct thread *', 'q_link'): 1125 print(GetThreadSummary(th), O=O) 1126 count += 1 1127 print("{0: <d} entries!".format(count)) 1128 1129#EndMacro: dumpcrashed_thread_queue 1130 1131#Macro: dumpcallqueue 1132 1133@lldb_command('dumpcallqueue') 1134def DumpCallQueue(cmd_args=None): 1135 """ Displays the contents of the specified call_entry queue. 1136 Usage: dumpcallqueue <queue_head_t *> 1137 """ 1138 if cmd_args is None or len(cmd_args) == 0: 1139 raise ArgumentError("Invalid arguments") 1140 1141 print("{0: <18s} {1: <18s} {2: <18s} {3: <64s} {4: <18s}".format('CALL_ENTRY', 'PARAM0', 'PARAM1', 'DEADLINE', 'FUNC')) 1142 callhead = kern.GetValueFromAddress(cmd_args[0], 'queue_head_t *') 1143 count = 0 1144 for callentry in IterateQueue(callhead, 'struct call_entry *', 'q_link'): 1145 print("{0: <#18x} {1: <#18x} {2: <#18x} {3: <64d} {4: <#18x}".format( 1146 unsigned(callentry), unsigned(callentry.param0), unsigned(callentry.param1), 1147 unsigned(callentry.deadline), unsigned(callentry.func))) 1148 count += 1 1149 print("{0: <d} entries!".format(count)) 1150 1151#EndMacro: dumpcallqueue 1152 1153@lldb_command('showalltasklogicalwrites') 1154def ShowAllTaskIOStats(cmd_args=None): 1155 """ Commad to print I/O stats for all tasks 1156 """ 1157 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")) 1158 for t in kern.tasks: 1159 pval = GetProcFromTask(t) 1160 print("{0: <#18x} {1: >20d} {2: >20d} {3: >20d} {4: >20d} {5: <20s} {6: <20s} {7: <20s} {8: <20s} {9: <20s}".format(t, 1161 t.task_writes_counters_internal.task_immediate_writes, 1162 t.task_writes_counters_internal.task_deferred_writes, 1163 t.task_writes_counters_internal.task_invalidated_writes, 1164 t.task_writes_counters_internal.task_metadata_writes, 1165 t.task_writes_counters_external.task_immediate_writes, 1166 t.task_writes_counters_external.task_deferred_writes, 1167 t.task_writes_counters_external.task_invalidated_writes, 1168 t.task_writes_counters_external.task_metadata_writes, 1169 GetProcName(pval))) 1170 1171 1172@lldb_command('showalltasks','C R', fancy=True) 1173def ShowAllTasks(cmd_args=None, cmd_options={}, O=None): 1174 """ Routine to print a summary listing of all the tasks 1175 wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" 1176 if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung 1177 io_policy -> RAGE - rapid aging of vnodes requested 1178 NORM - normal I/O explicitly requested (this is the default) 1179 PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) 1180 THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) 1181 Usage: (lldb) showalltasks -C : describe the corpse structure 1182 Usage: (lldb) showalltasks -R : describe the task role app status 1183 """ 1184 global kern 1185 extra_hdr = '' 1186 showcorpse = False 1187 showrole = False 1188 if '-C' in cmd_options: 1189 showcorpse = True 1190 extra_hdr += " " + GetKCDataSummary.header 1191 1192 if '-R' in cmd_options: 1193 showrole = True 1194 extra_hdr += " " + GetTaskRoleSummary.header 1195 1196 with O.table(GetTaskSummary.header + extra_hdr + " " + GetProcSummary.header): 1197 for t in kern.tasks: 1198 pval = GetProcFromTask(t) 1199 print(GetTaskSummary(t, showcorpse) + " " + (GetTaskRoleSummary(t) + " " if showrole else "") 1200 + GetProcSummary(pval)) 1201 1202 ZombTasks() 1203 1204def TaskForPmapHelper(pmap) -> Optional[value]: 1205 """ Given a pmap pointer, return the task pointer which contains that 1206 address space. 1207 1208 pmap: PMAP pointer whose task to find. 1209 """ 1210 for tasklist in [kern.tasks, kern.terminated_tasks]: 1211 for task in tasklist: 1212 if kern.GetValueFromAddress(unsigned(task.map.pmap), 'pmap_t') == pmap: 1213 return task 1214 1215 return None 1216 1217@lldb_command('taskforpmap') 1218def TaskForPmap(cmd_args=None): 1219 """ Find the task whose pmap corresponds to <pmap>. 1220 Syntax: (lldb) taskforpmap <pmap> 1221 Multiple -v's can be specified for increased verbosity 1222 """ 1223 if cmd_args is None or len(cmd_args) == 0: 1224 raise ArgumentError("Too few arguments to taskforpmap.") 1225 pmap = kern.GetValueFromAddress(cmd_args[0], 'pmap_t') 1226 task = TaskForPmapHelper(pmap) 1227 1228 if task is None: 1229 print("Couldn't find task for pmap {:#x}".format(pmap)) 1230 return 1231 1232 print(GetTaskSummary.header + " " + GetProcSummary.header) 1233 pval = GetProcFromTask(task) 1234 print(GetTaskSummary(task) + " " + GetProcSummary(pval)) 1235 1236@lldb_command('showterminatedtasks') 1237def ShowTerminatedTasks(cmd_args=None): 1238 """ Routine to print a summary listing of all the terminated tasks 1239 wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" 1240 if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung 1241 io_policy -> RAGE - rapid aging of vnodes requested 1242 NORM - normal I/O explicitly requested (this is the default) 1243 PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) 1244 THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) 1245 syntax: (lldb)showallterminatedtasks 1246 """ 1247 global kern 1248 print(GetTaskSummary.header + " " + GetProcSummary.header) 1249 for t in kern.terminated_tasks: 1250 1251 # If the task has been terminated it's likely that the process is 1252 # gone too. If there is no proc it may still be possible to find 1253 # the original proc name. 1254 pval = GetProcFromTask(t) 1255 if pval is not None: 1256 psummary = GetProcSummary(pval) 1257 else: 1258 name = GetProcNameForTask(t); 1259 pslen = GetProcSummary.header.find("command"); 1260 psummary = "{0: <{indent}} {1: <s}".format("", name, indent = pslen - 1) 1261 1262 print(GetTaskSummary(t) + " " + psummary) 1263 1264 return True 1265 1266# Macro: showtaskstacks 1267 1268def TokenExistsInStack(thread, regex): 1269 thread_val = GetLLDBThreadForKernelThread(thread) 1270 for frame in thread_val.frames: 1271 if frame.GetFunction(): 1272 func_name = frame.GetFunctionName() 1273 func_arguments = frame.arguments 1274 matching_argument = any( 1275 True if regex.search(str(arg.type)) or regex.search(str(arg.value)) else False 1276 for arg in func_arguments 1277 ) 1278 if regex.search(func_name) or matching_argument: 1279 return True 1280 return False 1281 1282def ShowTaskStacks(task, O=None, regex=None): 1283 """ Print a task with summary and stack information for each of its threads 1284 """ 1285 global kern 1286 first = True 1287 1288 for th in IterateQueue(task.threads, 'thread *', 'task_threads'): 1289 if regex is None or TokenExistsInStack(th, regex): 1290 if first: 1291 print(GetTaskSummary.header + " " + GetProcSummary.header) 1292 pval = GetProcFromTask(task) 1293 print(GetTaskSummary(task) + " " + GetProcSummary(pval)) 1294 first = False 1295 with O.table(GetThreadSummary.header, indent=True): 1296 print(GetThreadSummary(th, O=O)) 1297 print(GetThreadBackTrace(th, prefix=" ") + "\n") 1298 1299 1300def GetAllTasks(): 1301 """ Generator that yields all tasks (both active and zombie). 1302 returns: 1303 Generator of (task, proc) tuples for all tasks 1304 """ 1305 # Yield active tasks 1306 for t in kern.tasks: 1307 pval = GetProcFromTask(t) 1308 yield (t, pval) 1309 1310 # Yield zombie tasks 1311 for proc in kern.zombprocs: 1312 if proc.p_stat != 5: # Skip if process state is 5 (SIDL - intermediate state during process creation) 1313 t = GetTaskFromProc(proc) 1314 if t is not None: 1315 yield (t, proc) 1316 1317def FindTasksByName(searchstr, ignore_case=True): 1318 """ Search the list of tasks by name. 1319 params: 1320 searchstr: str - a regex like string to search for task 1321 ignore_case: bool - If False then exact matching will be enforced 1322 returns: 1323 [] - array of task object. Empty if not found any 1324 """ 1325 re_options = 0 1326 if ignore_case: 1327 re_options = re.IGNORECASE 1328 search_regex = re.compile(searchstr, re_options) 1329 retval = [] 1330 1331 for task, proc in GetAllTasks(): 1332 if proc is not None: 1333 process_name = "{:s}".format(GetProcName(proc)) 1334 if search_regex.search(process_name): 1335 retval.append(task) 1336 1337 return retval 1338 1339@lldb_command('showtaskstacks', 'F:', fancy=True) 1340def ShowTaskStacksCmdHelper(cmd_args=None, cmd_options={}, O=None): 1341 """ Routine to print out the stack for each thread in a task 1342 Usage: showtaskstacks <0xaddress of task> 1343 or: showtaskstacks -F launchd 1344 """ 1345 1346 if "-F" in cmd_options: 1347 find_task_str = cmd_options["-F"] 1348 task_list = FindTasksByName(find_task_str) 1349 for tval in task_list: 1350 ShowTaskStacks(tval, O=O) 1351 return 1352 1353 if cmd_args is None or len(cmd_args) == 0: 1354 raise ArgumentError("No arguments passed") 1355 1356 tval = kern.GetValueFromAddress(cmd_args[0], 'task *') 1357 if not tval: 1358 raise ArgumentError("unknown arguments: {:s}".format(str(cmd_args))) 1359 else: 1360 ShowTaskStacks(tval, O=O) 1361 1362# EndMacro: showtaskstacks 1363 1364def CheckTaskProcRefs(task, proc, O=None): 1365 btlib = kmemory.BTLibrary.get_shared() 1366 1367 for thread in IterateQueue(task.threads, 'thread *', 'task_threads'): 1368 uthread = GetBSDThread(thread) 1369 refcount = int(uthread.uu_proc_refcount) 1370 uu_ref_info = uthread.uu_proc_ref_info 1371 if int(uu_ref_info) == 0: 1372 continue 1373 uu_ref_index = int(uu_ref_info.upri_pindex) 1374 if refcount == 0: 1375 continue 1376 for ref in range(0, uu_ref_index): 1377 if unsigned(uu_ref_info.upri_proc_ps[ref]) == unsigned(proc): 1378 print(GetTaskSummary.header + " " + GetProcSummary.header) 1379 pval = GetProcFromTask(task) 1380 print(GetTaskSummary(task) + " " + GetProcSummary(pval)) 1381 with O.table(GetThreadSummary.header, indent=True): 1382 print(GetThreadSummary(thread, O=O)) 1383 1384 bts = btlib.get_stack(unsigned(uu_ref_info.upri_proc_stacks[ref])) 1385 print(*bts.symbolicated_frames(), sep="\n") 1386 1387@lldb_command('showprocrefs', fancy=True) 1388def ShowProcRefs(cmd_args=None, cmd_options={}, O=None): 1389 """ Display information on threads/BTs that could be holding a reference on the specified proc 1390 NOTE: We can't say affirmatively if any of these references are still held since 1391 there's no way to pair references with drop-refs in the current infrastructure. 1392 Usage: showprocrefs <proc> 1393 """ 1394 if cmd_args is None or len(cmd_args) == 0: 1395 raise ArgumentError("No arguments passed") 1396 1397 proc = kern.GetValueFromAddress(cmd_args[0], 'proc *') 1398 1399 for t in kern.tasks: 1400 CheckTaskProcRefs(t, proc, O=O) 1401 for t in kern.terminated_tasks: 1402 CheckTaskProcRefs(t, proc, O=O) 1403 1404@lldb_command('showallthreads', fancy=True) 1405def ShowAllThreads(cmd_args=None, cmd_options={}, O=None): 1406 """ Display info about all threads in the system 1407 """ 1408 1409 # Terminated threads get prefixed with a 'T' 1410 def ShowTaskTerminatedThreads(task, O=O): 1411 tlist = tmap.get(unsigned(task), []) 1412 for thval in tlist: 1413 print("T\t" + GetThreadSummary(thval, O=O)) 1414 1415 # Task -> [thread, ..] map of terminated threads 1416 tmap = defaultdict(list) 1417 for thr in kern.terminated_threads: 1418 tmap[unsigned(thr.t_tro.tro_task)].append(thr) 1419 1420 for t in kern.tasks: 1421 ShowTaskThreads([str(int(t))], O=O) 1422 ShowTaskTerminatedThreads(t, O=O) 1423 print(" \n") 1424 1425 for t in kern.terminated_tasks: 1426 print("Terminated: \n") 1427 ShowTaskThreads([str(int(t))], O=O) 1428 ShowTaskTerminatedThreads(t, O=O) 1429 print(" \n") 1430 1431 return 1432 1433@lldb_command('showterminatedthreads', fancy=True) 1434def ShowTerminatedThreads(cmd_args=None, cmd_options={}, O=None): 1435 """ Display info about all terminated threads in the system 1436 """ 1437 1438 with O.table(GetThreadSummary.header, indent=True): 1439 for t in kern.terminated_threads: 1440 print(GetThreadSummary(t, O=O)) 1441 1442 1443@lldb_command('showtaskthreads', "F:", fancy=True) 1444def ShowTaskThreads(cmd_args = None, cmd_options={}, O=None): 1445 """ List the threads of a task. 1446 Usage: showtaskthreads <task-ptr> 1447 or: showtaskthreads -F <name> 1448 """ 1449 task_list = [] 1450 1451 if "-F" in cmd_options: 1452 task_list = FindTasksByName(cmd_options["-F"]) 1453 elif cmd_args: 1454 task_addr = ArgumentStringToInt(cmd_args[0]) 1455 t = addressof(kern.CreateValueFromAddress(task_addr, 'task')) 1456 task_list = [t] 1457 else: 1458 raise ArgumentError("No arguments passed") 1459 1460 for task in task_list: 1461 print(GetTaskSummary.header + " " + GetProcSummary.header) 1462 pval = GetProcFromTask(task) 1463 print(GetTaskSummary(task) + " " + GetProcSummary(pval)) 1464 with O.table(GetThreadSummary.header, indent=True): 1465 for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): 1466 print(GetThreadSummary(thval, O=O)) 1467 return 1468 1469@lldb_command('showact', fancy=True) 1470def ShowAct(cmd_args=None, cmd_options={}, O=None): 1471 """ Routine to print out the state of a specific thread. 1472 usage: showact <activation> 1473 """ 1474 if cmd_args is None or len(cmd_args) == 0: 1475 raise ArgumentError("No arguments passed") 1476 threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') 1477 with O.table(GetThreadSummary.header): 1478 print(GetThreadSummary(threadval, O=O)) 1479 1480@lldb_command('showactstack', fancy=True) 1481def ShowActStack(cmd_args=None, cmd_options={}, O=None): 1482 """ Routine to print out the stack of a specific thread. 1483 usage: showactstack <activation> 1484 """ 1485 if cmd_args is None or len(cmd_args) == 0: 1486 raise ArgumentError("No arguments passed") 1487 threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') 1488 with O.table(GetThreadSummary.header): 1489 print(GetThreadSummary(threadval, O=O)) 1490 print(GetThreadBackTrace(threadval, prefix="\t")) 1491 return 1492 1493@lldb_command('switchtoact', fancy=True) 1494def SwitchToAct(cmd_args=None, cmd_options={}, O=None): 1495 """ Switch to different context specified by activation 1496 This command allows gdb to examine the execution context and call 1497 stack for the specified activation. For example, to view the backtrace 1498 for an activation issue "switchtoact <address>", followed by "bt". 1499 Before resuming execution, issue a "resetctx" command, to 1500 return to the original execution context. 1501 """ 1502 if cmd_args is None or len(cmd_args) == 0: 1503 raise ArgumentError("No arguments passed") 1504 thval = kern.GetValueFromAddress(cmd_args[0], 'thread *') 1505 lldbthread = GetLLDBThreadForKernelThread(thval) 1506 with O.table(GetThreadSummary.header): 1507 print(GetThreadSummary(thval, O=O)) 1508 LazyTarget.GetProcess().selected_thread = lldbthread 1509 if not LazyTarget.GetProcess().SetSelectedThread(lldbthread): 1510 print("Failed to switch thread.") 1511 return 1512 1513@lldb_command('switchtoregs') 1514def SwitchToRegs(cmd_args=None): 1515 """ Routine to switch to a register state. 1516 Usage: (lldb) switchtoregs <struct arm_saved_state[64] *> 1517 This command creates a fake thread in lldb with the saved register state. 1518 Note: This command ONLY works for ARM based kernel setup. 1519 """ 1520 1521 if cmd_args is None or len(cmd_args) == 0: 1522 raise ArgumentError("No arguments passed") 1523 1524 lldb_process = LazyTarget.GetProcess() 1525 1526 saved_state = ArgumentStringToInt(cmd_args[0]) 1527 # any change to this logic requires change in operating_system.py as well 1528 fake_thread_id = 0xdead0000 | (saved_state & ~0xffff0000) 1529 fake_thread_id = fake_thread_id & 0xdeadffff 1530 lldb_process.CreateOSPluginThread(0xdeadbeef, saved_state) 1531 lldbthread = lldb_process.GetThreadByID(int(fake_thread_id)) 1532 1533 if not lldbthread.IsValid(): 1534 print("Failed to create thread") 1535 return 1536 1537 lldb_process.selected_thread = lldbthread 1538 if not lldb_process.SetSelectedThread(lldbthread): 1539 print("Failed to switch thread") 1540 print("Switched to Fake thread created from register state at {:#x}".format( 1541 saved_state)) 1542 1543# Macro: showcallchains 1544CallChainNode = namedtuple("CallChainNode", "callers threads") 1545 1546def GatherCallChainsDFS(cur_node: CallChainNode, call_chains, cur_path): 1547 if cur_node.threads: 1548 call_chain = " <- ".join(cur_path) 1549 call_chains[call_chain] = cur_node.threads 1550 1551 for next_func_name, next_node in cur_node.callers.items(): 1552 cur_path.append(next_func_name) 1553 GatherCallChainsDFS(next_node, call_chains, cur_path) 1554 cur_path.pop() 1555 1556 1557def GetCallChains(filter_regex) -> dict[str, list[str]]: 1558 ## Filter threads and build call graph 1559 root = CallChainNode({"root": CallChainNode({}, [])}, []) 1560 1561 zomb_tasks = [t for proc in kern.zombprocs if (t := GetTaskFromProc(proc)) is not None] 1562 for t in kern.tasks + zomb_tasks: 1563 for th in IterateQueue(t.threads, 'thread *', 'task_threads'): 1564 thread_val = GetLLDBThreadForKernelThread(th) 1565 cur_node = root 1566 prev_func_name = "root" 1567 matched = False 1568 1569 for frame in thread_val.frames: 1570 if frame.GetFunction(): 1571 func_name = frame.GetFunctionName() 1572 func_arguments = frame.arguments 1573 matching_argument = any( 1574 True if filter_regex.match(str(arg.type)) or filter_regex.match(str(arg.value)) else False 1575 for arg in func_arguments 1576 ) 1577 if filter_regex.match(func_name) or matching_argument: 1578 matched = True 1579 1580 callers = cur_node.callers[prev_func_name].callers 1581 callers[func_name] = callers.get(func_name, CallChainNode({}, [])) 1582 1583 cur_node = cur_node.callers[prev_func_name] 1584 prev_func_name = func_name 1585 1586 if matched: 1587 cur_node.callers[prev_func_name].threads.append(th) 1588 1589 ## gather call chains 1590 call_chains: dict[str, list[str]] = {} 1591 GatherCallChainsDFS(root.callers["root"], call_chains, []) 1592 1593 return call_chains 1594 1595CallChainThreadInfo = namedtuple('CallChainThreadInfo', ['hex', 'state', 'base', 'pri', 'since_off', 'wait_evt', 'wait_evt_sym', 'thread_name', 'task_name']) 1596 1597@lldb_command('showcallchains', fancy=True) 1598def ShowCallChains(cmd_args=None, cmd_options={}, O=None): 1599 """Routine to print out thread IDs, bucketized by function call chains 1600 1601 Usage: showcallchains <regex> 1602 The regex filters function names. Function names that don't match the regex are ignored. 1603 """ 1604 1605 if cmd_args is None or len(cmd_args) == 0: 1606 raise ArgumentError("No arguments passed.") 1607 1608 show_call_chain(cmd_args[0],O) 1609 1610def show_call_chain(param, O=None): 1611 1612 try: 1613 regex = re.compile(param) 1614 except: 1615 raise ArgumentError("Invalid predicate regex passed: {}".format(param[0])) 1616 1617 call_chains = GetCallChains(regex) 1618 1619 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}" 1620 header = summary_str.format(info=CallChainThreadInfo('thread', 'state', 'base', 'pri', 'since-off (us)', 'wait_evt', 'wait_evt_sym', 'thread_name', 'task_name')) 1621 1622 ## sort desc by time_since_off 1623 from scheduler import GetSchedMostRecentDispatch 1624 most_recent_dispatch = GetSchedMostRecentDispatch(False) 1625 def GetTimeSinceOff(th): 1626 last_off = th.last_run_time 1627 time_since_off_abs = unsigned(most_recent_dispatch - last_off) 1628 return time_since_off_abs 1629 1630 for call_chain, threads in call_chains.copy().items(): 1631 call_chains[call_chain] = sorted( 1632 zip(threads, (GetTimeSinceOff(th) for th in threads)), 1633 reverse=True, 1634 key=lambda a: a[1] 1635 ) 1636 1637 ## print results 1638 for call_chain, threads in sorted( 1639 list(call_chains.items()), 1640 key = lambda a: len(a[1]) 1641 ): 1642 print("{0}, {1} thread{2}".format(call_chain, len(threads), len(threads) > 1 and "s" or "")) 1643 1644 with O.table(header, indent=True): 1645 for th, time_since_off_abs in threads: 1646 thread_hex = '{:<#018x}'.format(th) 1647 base_priority = str(int(th.base_pri)) 1648 sched_priority = str(int(th.sched_pri)) 1649 1650 state = int(th.state) 1651 state_str = '' 1652 mask = 0x1 1653 while mask <= LAST_THREAD_STATE: 1654 state_str += THREAD_STATE_CHARS[int(state & mask)] 1655 mask <<= 1 1656 if int(th.inspection): 1657 state_str += 'C' 1658 1659 wait_event_str = '' 1660 wait_event_str_sym = '' 1661 if state & 0x1: # WAIT 1662 wait_event_str = '{:<#018x}'.format(unsigned(th.wait_event)) 1663 wait_event_str_sym = kern.Symbolicate(int(hex(th.wait_event), 16)) 1664 1665 time_since_off_us = "{:,}".format(kern.GetNanotimeFromAbstime(time_since_off_abs) / 1000.0) 1666 1667 uthread = GetBSDThread(th) 1668 thread_name = GetThreadNameFromBSDThread(uthread) 1669 1670 pval = GetProcFromTask(th.t_tro.tro_task) 1671 task_name = GetProcName(pval) 1672 1673 info = CallChainThreadInfo(hex=thread_hex, state=state_str, base=base_priority, pri=sched_priority, since_off=time_since_off_us, 1674 wait_evt=wait_event_str, wait_evt_sym=wait_event_str_sym, thread_name=thread_name, task_name=task_name) 1675 info_str = summary_str.format(info=info) 1676 1677 print(O.format(info_str)) 1678 print("") 1679 1680 print("Summary:") 1681 print("{} different call chains".format(len(call_chains))) 1682# Endmacro showcallchains 1683 1684 1685# Macro: showallstacks 1686@lldb_command('showallstacks', "R:", fancy=True) 1687def ShowAllStacks(cmd_args=None, cmd_options={}, O=None): 1688 """Routine to print out the stack for each thread in the system. 1689 Usage: showallstacks [-R REGEX] 1690 -R : Only show stacks with function names matching the provided regular expression 1691 """ 1692 if "-R" in cmd_options: 1693 regex = re.compile(cmd_options['-R']) 1694 else: 1695 regex = None 1696 for t in kern.tasks: 1697 ShowTaskStacks(t, O=O, regex=regex) 1698 if regex is None: 1699 print(" \n") 1700 1701 ShowZombStacks(O=O, regex=regex) 1702# EndMacro: showallstacks 1703 1704# Macro: showcurrentstacks 1705@lldb_command('showcurrentstacks', fancy=True) 1706def ShowCurrentStacks(cmd_args=None, cmd_options={}, O=None): 1707 """ Routine to print out the thread running on each cpu (incl. its stack) 1708 """ 1709 processor_list = kern.GetGlobalVariable('processor_list') 1710 current_processor = processor_list 1711 while unsigned(current_processor) > 0: 1712 print("\n" + GetProcessorSummary(current_processor)) 1713 active_thread = current_processor.active_thread 1714 if unsigned(active_thread) != 0: 1715 task_val = active_thread.t_tro.tro_task 1716 proc_val = GetProcFromTask(task_val) 1717 print(GetTaskSummary.header + " " + GetProcSummary.header) 1718 print(GetTaskSummary(task_val) + " " + GetProcSummary(proc_val)) 1719 with O.table(GetThreadSummary.header, indent=True): 1720 print(GetThreadSummary(active_thread, O=O)) 1721 print("\tBacktrace:") 1722 print(GetThreadBackTrace(active_thread, prefix="\t")) 1723 current_processor = current_processor.processor_list 1724 return 1725# EndMacro: showcurrentstacks 1726 1727@lldb_command('showcurrentthreads', fancy=True) 1728def ShowCurrentThreads(cmd_args=None, cmd_options={}, O=None): 1729 """ Display info about threads running on each cpu """ 1730 processor_list = kern.GetGlobalVariable('processor_list') 1731 current_processor = processor_list 1732 while unsigned(current_processor) > 0: 1733 print(GetProcessorSummary(current_processor)) 1734 active_thread = current_processor.active_thread 1735 if unsigned(active_thread) != 0 : 1736 task_val = active_thread.t_tro.tro_task 1737 proc_val = GetProcFromTask(task_val) 1738 print(GetTaskSummary.header + " " + GetProcSummary.header) 1739 print(GetTaskSummary(task_val) + " " + GetProcSummary(proc_val)) 1740 with O.table(GetThreadSummary.header, indent=True): 1741 print(GetThreadSummary(active_thread, O=O)) 1742 current_processor = current_processor.processor_list 1743 return 1744 1745def GetFullBackTrace(frame_addr, verbosity = vHUMAN, prefix = ""): 1746 """ Get backtrace across interrupt context. 1747 params: frame_addr - int - address in memory which is a frame pointer (ie. rbp, r7) 1748 prefix - str - prefix for each line of output. 1749 1750 """ 1751 out_string = "" 1752 bt_count = 0 1753 frame_ptr = frame_addr 1754 previous_frame_ptr = 0 1755 while frame_ptr and frame_ptr != previous_frame_ptr and bt_count < 128: 1756 pc_val = kern.GetValueFromAddress(frame_ptr + kern.ptrsize,'uintptr_t *') 1757 pc_val = kern.StripKernelPAC(unsigned(dereference(pc_val))) 1758 out_string += prefix + GetSourceInformationForAddress(pc_val) + "\n" 1759 bt_count +=1 1760 previous_frame_ptr = frame_ptr 1761 frame_val = kern.GetValueFromAddress((frame_ptr), 'uintptr_t *') 1762 if unsigned(frame_val) == 0: 1763 break 1764 frame_ptr = unsigned(dereference(frame_val)) 1765 1766 return out_string 1767 1768@lldb_command('fullbt') 1769def FullBackTrace(cmd_args=[]): 1770 """ Show full backtrace across the interrupt boundary. 1771 Syntax: fullbt <frame ptr> 1772 Example: fullbt `$rbp` 1773 """ 1774 if len(cmd_args) < 1: 1775 raise ArgumentError() 1776 1777 print(GetFullBackTrace(ArgumentStringToInt(cmd_args[0]), prefix="\t")) 1778 1779@lldb_command('fullbtall', fancy=True) 1780def FullBackTraceAll(cmd_args=[], cmd_options={}, O=None): 1781 """ Show full backtrace across the interrupt boundary for threads running on all processors. 1782 Syntax: fullbtall 1783 Example: fullbtall 1784 """ 1785 for processor in IterateLinkedList(kern.globals.processor_list, 'processor_list') : 1786 print("\n" + GetProcessorSummary(processor)) 1787 active_thread = processor.active_thread 1788 if unsigned(active_thread) != 0 : 1789 task_val = active_thread.t_tro.tro_task 1790 proc_val = GetProcFromTask(task_val) 1791 print(GetTaskSummary.header + " " + GetProcSummary.header) 1792 print(GetTaskSummary(task_val) + " " + GetProcSummary(proc_val)) 1793 with O.table(GetThreadSummary.header, indent=True): 1794 print(GetThreadSummary(active_thread, O=O)) 1795 print("\tBacktrace:") 1796 1797 ThreadVal = GetLLDBThreadForKernelThread(active_thread) 1798 1799 FramePtr = ThreadVal.frames[0].GetFP() 1800 1801 print(GetFullBackTrace(unsigned(FramePtr), prefix="\t")) 1802 1803 1804@lldb_command('symbolicate') 1805def SymbolicateAddress(cmd_args=[]): 1806 """ Symbolicate an address for symbol information from loaded symbols 1807 Example: "symbolicate 0xaddr" is equivalent to "output/a 0xaddr" 1808 """ 1809 if len(cmd_args) < 1: 1810 print("Invalid address.\nSyntax: symbolicate <address>") 1811 return False 1812 print(GetSourceInformationForAddress(ArgumentStringToInt(cmd_args[0]))) 1813 return True 1814 1815@lldb_command('showinitchild') 1816def ShowInitChild(cmd_args=None): 1817 """ Routine to print out all processes in the system 1818 which are children of init process 1819 """ 1820 headp = kern.globals.initproc.p_children 1821 for pp in IterateListEntry(headp, 'p_sibling'): 1822 print(GetProcInfo(pp)) 1823 return 1824 1825@lldb_command('showproctree') 1826def ShowProcTree(cmd_args=None): 1827 """ Routine to print the processes in the system in a hierarchical tree form. This routine does not print zombie processes. 1828 If no argument is given, showproctree will print all the processes in the system. 1829 If pid is specified, showproctree prints all the descendants of the indicated process 1830 """ 1831 search_pid = 0 1832 if cmd_args: 1833 search_pid = ArgumentStringToInt(cmd_args[0]) 1834 1835 if search_pid < 0: 1836 raise ArgumentError("pid specified must be a positive number") 1837 1838 hdr_format = "{0: <6s} {1: <14s} {2: <9s}\n" 1839 out_string = hdr_format.format("PID", "PROCESS", "POINTER") 1840 out_string += hdr_format.format('='*3, '='*7, '='*7) 1841 proc = GetProcForPid(search_pid) 1842 out_string += "{0: <6d} {1: <32s} [ {2: #019x} ]\n".format( 1843 proc.p_ppid, GetProcName(proc.p_pptr), unsigned(proc.p_pptr)) 1844 out_string += "|--{0: <6d} {1: <32s} [ {2: #019x} ]\n".format( 1845 GetProcPID(proc), GetProcName(proc), unsigned(proc)) 1846 print(out_string) 1847 ShowProcTreeRecurse(proc, "| ") 1848 1849def ShowProcTreeRecurse(proc, prefix=""): 1850 """ Prints descendants of a given proc in hierarchial tree form 1851 params: 1852 proc : core.value representing a struct proc * in the kernel 1853 returns: 1854 str : String containing info about a given proc and its descendants in tree form 1855 """ 1856 if proc.p_childrencnt > 0: 1857 head_ptr = proc.p_children.lh_first 1858 1859 for p in IterateListEntry(proc.p_children, 'p_sibling'): 1860 print(prefix + "|--{0: <6d} {1: <32s} [ {2: #019x} ]\n".format( 1861 GetProcPID(p), GetProcName(p), unsigned(p))) 1862 ShowProcTreeRecurse(p, prefix + "| ") 1863 1864@lldb_command('showthreadfortid', fancy=True) 1865def ShowThreadForTid(cmd_args=None, O=None): 1866 """ The thread structure contains a unique thread_id value for each thread. 1867 This command is used to retrieve the address of the thread structure(thread_t) 1868 corresponding to a given thread_id. 1869 """ 1870 if cmd_args is None or len(cmd_args) == 0: 1871 raise ArgumentError("Please provide thread_t whose tid you'd like to look up") 1872 1873 search_tid = ArgumentStringToInt(cmd_args[0]) 1874 for taskp in kern.tasks: 1875 for actp in IterateQueue(taskp.threads, 'struct thread *', 'task_threads'): 1876 if search_tid == int(actp.thread_id): 1877 print("Found {0: #019x}".format(actp)) 1878 with O.table(GetThreadSummary.header): 1879 print(GetThreadSummary(actp, O=O)) 1880 return 1881 print("Not a valid thread_id") 1882 1883def GetProcessorSummary(processor): 1884 """ Internal function to print summary of processor 1885 params: processor - value representing struct processor * 1886 return: str - representing the details of given processor 1887 """ 1888 1889 processor_state_str = "INVALID" 1890 processor_state = int(processor.state) 1891 1892 processor_states = { 1893 0: 'OFF_LINE', 1894 1: 'SHUTDOWN', 1895 2: 'START', 1896 3: 'PENDING_OFFLINE', 1897 4: 'IDLE', 1898 5: 'DISPATCHING', 1899 6: 'RUNNING' 1900 } 1901 1902 if processor_state in processor_states: 1903 processor_state_str = "{0: <11s} ".format(processor_states[processor_state]) 1904 1905 processor_recommended_str = "" 1906 if int(processor.is_recommended) == 0: 1907 processor_recommended_str = " (not recommended)" 1908 1909 ast = 0 1910 preemption_disable = 0 1911 preemption_disable_str = "" 1912 1913 if kern.arch == 'x86_64': 1914 cpu_data = kern.globals.cpu_data_ptr[processor.cpu_id] 1915 if (cpu_data != 0) : 1916 ast = cpu_data.cpu_pending_ast 1917 preemption_disable = cpu_data.cpu_preemption_level 1918 # On arm64, it's kern.globals.CpuDataEntries[processor.cpu_id].cpu_data_vaddr 1919 # but LLDB can't find CpuDataEntries... 1920 1921 ast_str = GetASTSummary(ast) 1922 1923 if (preemption_disable != 0) : 1924 preemption_disable_str = "Preemption Disabled" 1925 1926 processor_reasons = { 1927 0: '(REASON_NONE)', 1928 1: '(REASON_SYSTEM)', 1929 2: '(REASON_USER)', 1930 3: '(REASON_CLPC_SYSTEM)', 1931 4: '(REASON_CLPC_USER)' 1932 } 1933 1934 processor_shutdown_reason_str = ""; 1935 processor_shutdown_reason = int(processor.last_shutdown_reason) 1936 1937 if processor_state in {0, 1, 3}: 1938 processor_shutdown_reason_str = processor_reasons[processor_shutdown_reason] 1939 1940 out_str = "Processor {: <#018x} cpu_id {:>#4x} AST: {:<6s} State {:<s}{:<s}{:<s} {:<s}\n".format( 1941 processor, int(processor.cpu_id), ast_str, processor_state_str, processor_shutdown_reason_str, 1942 processor_recommended_str, preemption_disable_str) 1943 return out_str 1944 1945ledger_limit_infinity = (uint64_t(0x1).value << 63) - 1 1946 1947def GetLedgerEntryIndex(template, name): 1948 i = 0 1949 lt_count = template.lt_cnt 1950 lt_entries = template.lt_entries 1951 1952 while i != lt_count: 1953 if str(lt_entries[i].et_key) == name: 1954 return i 1955 i += 1 1956 return -1 1957 1958def GetLedgerEntryWithTemplate(ledger_template, ledgerp, i): 1959 """ Internal function to get internals of a ledger entry (*not* a ledger itself) 1960 params: ledger_template - value representing struct ledger_template_t for the task or thread 1961 ledgerp - value representing ledger pointer 1962 i - index in ledger 1963 return: entry - entry dictionary 1964 """ 1965 lf_refill_scheduled = 0x0400 1966 lf_tracking_max = 0x4000 1967 lf_is_counter = 0x80000 1968 1969 entry = {} 1970 1971 et = ledger_template.lt_entries[i] 1972 entry["key"] = str(et.et_key) 1973 et_size = et.et_size 1974 if et_size == sizeof("struct ledger_entry_small"): 1975 les = ledgerp.l_entries[et.et_offset] 1976 flags = int(les.les_flags) 1977 entry["debit"] = 0 1978 entry["flags"] = flags 1979 entry["limit"] = ledger_limit_infinity 1980 if (flags & lf_is_counter) and (hasattr(ledger_template, "lt_counters")): 1981 credit = 0 1982 for v in memory.IterateZPerCPU(cast(les.les_credit, "scalable_counter_t")): 1983 credit += v 1984 entry["credit"] = credit 1985 else: 1986 entry["credit"] = unsigned(les.les_credit) 1987 elif et_size == sizeof("struct ledger_entry"): 1988 le = cast(addressof(ledgerp.l_entries[et.et_offset]), "struct ledger_entry *") 1989 entry["credit"] = unsigned(le.le_credit) 1990 entry["debit"] = unsigned(le.le_debit) 1991 le_flags = int(le.le_flags) 1992 if (le_flags & lf_tracking_max): 1993 if hasattr(le._le._le_max, "le_interval_max"): 1994 entry["interval_max"] = unsigned(le._le._le_max.le_interval_max) 1995 entry["lifetime_max"] = unsigned(le._le._le_max.le_lifetime_max) 1996 1997 entry["limit"] = unsigned(le.le_limit) 1998 1999 if (le_flags & lf_refill_scheduled): 2000 entry["refill_period"] = unsigned(le._le.le_refill.le_refill_period) 2001 2002 if (unsigned(le.le_warn_percent) < 65535): 2003 entry["warn_percent"] = unsigned (le.le_warn_percent * 100 / 65536) 2004 entry["flags"] = le_flags 2005 entry["diag_threshold_scaled"] = int(le.le_diag_threshold_scaled) 2006 else: 2007 return None 2008 2009 entry["balance"] = entry["credit"] - entry["debit"] 2010 return entry 2011 2012def GetLedgerEntryWithName(ledger_template, ledger, name): 2013 idx = GetLedgerEntryIndex(ledger_template, name) 2014 assert(idx != -1) 2015 return GetLedgerEntryWithTemplate(ledger_template, ledger, idx) 2016 2017def FormatLedgerEntrySummary(entry, i, show_footprint_interval_max=False): 2018 """ internal function to format a ledger entry into a string 2019 params: entry - A python dictionary containing the ledger entry 2020 return: str - formatted output information of ledger entries 2021 """ 2022 out_str = '' 2023 out_str += "{: >32s} {:<2d}:".format(entry["key"], i) 2024 out_str += "{: >15d} ".format(entry["balance"]) 2025 2026 if (show_footprint_interval_max): 2027 if "interval_max" in entry: 2028 out_str += "{:12d} ".format(entry["interval_max"]) 2029 else: 2030 out_str += " - " 2031 2032 if "lifetime_max" in entry: 2033 out_str += "{:14d} ".format(entry["lifetime_max"]) 2034 else: 2035 out_str += " - " 2036 2037 out_str += "{:12d} {:12d} ".format(entry["credit"], entry["debit"]) 2038 if entry.get('limit', unsigned(ledger_limit_infinity)) != unsigned(ledger_limit_infinity): 2039 out_str += "{:12d} ".format(unsigned(entry["limit"])) 2040 else: 2041 out_str += " - " 2042 2043 if "refill_period" in entry: 2044 out_str += "{:15d} ".format(entry["refill_period"]) 2045 if entry["refill_period"] != 0: 2046 out_str += "{:9d} ".format((entry["limit"] * 100) // entry["refill_period"]) 2047 else: 2048 out_str += "XXXXX - " 2049 else: 2050 out_str += " - " 2051 out_str += " - " 2052 2053 if "warn_percent" in entry: 2054 out_str += "{:9d} ".format(entry["warn_percent"]) 2055 else: 2056 out_str += " - " 2057 2058 if "limit" in entry: 2059 if entry["balance"] > entry["limit"]: 2060 out_str += " X " 2061 else: 2062 out_str += " " 2063 else: 2064 out_str += " " 2065 2066 out_str += "{:#8x}\n".format(entry["flags"]) 2067 return out_str 2068 2069def GetLedgerEntrySummary(ledger_template, ledger, i, show_footprint_interval_max=False): 2070 """ internal function to get internals of a ledger entry (*not* a ledger itself) 2071 params: ledger_template - value representing struct ledger_template_t for the task or thread 2072 ledger - value representing ledger pointer 2073 return: str - formatted output information of ledger entries 2074 """ 2075 entry = GetLedgerEntryWithTemplate(ledger_template, ledger, i) 2076 return FormatLedgerEntrySummary(entry, i) 2077 2078 2079def GetThreadLedgers(thread_val): 2080 """ Internal function to get a summary of ledger entries for the given thread 2081 params: thread_val - value representing struct thread * 2082 return: thread - python dictionary containing threads's ledger entries. This can 2083 be printed directly with FormatThreadLedgerSummmary or outputted as json. 2084 """ 2085 thread = {} 2086 thread["address"] = unsigned(thread_val) 2087 ledgerp = thread_val.t_threadledger 2088 thread["entries"] = [] 2089 if ledgerp: 2090 i = 0 2091 while i != ledgerp.l_template.lt_cnt: 2092 thread["entries"].append(GetLedgerEntryWithTemplate(kern.globals.thread_ledger_template, 2093 ledgerp, i)) 2094 i = i + 1 2095 return thread 2096 2097def FormatThreadLedgerSummary(thread): 2098 """ Internal function to print a thread's ledger entries 2099 params: thread - python dictionary containing thread's ledger entries 2100 return: str - formatted output information for ledger entries of the input thread 2101 """ 2102 out_str = " [{:#08x}]\n".format(thread["address"]) 2103 entries = thread["entries"] 2104 for i, entry in enumerate(entries): 2105 out_str += FormatLedgerEntrySummary(entry, i) 2106 return out_str 2107 2108def GetTaskLedgers(task_val): 2109 """ Internal function to get summary of ledger entries from the task and its threads 2110 params: task_val - value representing struct task * 2111 return: task - python dictionary containing tasks's ledger entries. This can 2112 be printed directly with FormatTaskLedgerSummary or outputted as json. 2113 """ 2114 task_ledgerp = task_val.ledger 2115 i = 0 2116 tasks = [] 2117 task = {} 2118 task["address"] = unsigned(task_val) 2119 2120 pval = GetProcFromTask(task_val) 2121 if pval is not None: 2122 task["name"] = GetProcName(pval) 2123 task["pid"] = int(GetProcPID(pval)) 2124 2125 task["entries"] = [] 2126 while i != task_ledgerp.l_template.lt_cnt: 2127 task["entries"].append(GetLedgerEntryWithTemplate(kern.globals.task_ledger_template, task_ledgerp, i)) 2128 i = i + 1 2129 2130 # Now walk threads 2131 task["threads"] = [] 2132 for thval in IterateQueue(task_val.threads, 'thread *', 'task_threads'): 2133 task["threads"].append(GetThreadLedgers(thval)) 2134 2135 return task 2136 2137@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( 2138 "task [thread]", "entry", "#", "balance", "lifetime_max", "credit", 2139 "debit", "limit", "refill period", "lim pct", "warn pct", "over?", "flags")) 2140def FormatTaskLedgerSummary(task, show_footprint_interval_max=False): 2141 """ Internal function to get summary of ledger entries from the task and its threads 2142 params: task_val - value representing struct task * 2143 return: str - formatted output information for ledger entries of the input task 2144 """ 2145 out_str = '' 2146 out_str += "{: #08x} ".format(task["address"]) 2147 if "name" in task: 2148 out_str += "{: <5s}:\n".format(task["name"]) 2149 else: 2150 out_str += "Invalid process\n" 2151 2152 for i, entry in enumerate(task["entries"]): 2153 out_str += FormatLedgerEntrySummary(entry, i, show_footprint_interval_max) 2154 2155 for thread in task["threads"]: 2156 out_str += FormatThreadLedgerSummary(thread) 2157 return out_str 2158 2159 2160# Macro: showtaskledgers 2161 2162@lldb_command('showtaskledgers', 'JF:I') 2163def ShowTaskLedgers(cmd_args=None, cmd_options={}): 2164 """ Routine to print a summary of ledger entries for the task and all of its threads 2165 or : showtaskledgers [ -I ] [-J] [ -F ] <task> 2166 options: 2167 -I: show footprint interval max (DEV/DEBUG only) 2168 -F: specify task via name instead of address 2169 -J: output json 2170 - 2171 """ 2172 print_json = False 2173 if "-F" in cmd_options: 2174 task_list = FindTasksByName(cmd_options["-F"]) 2175 for tval in task_list: 2176 print(FormatTaskLedgerSummary.header) 2177 ledgers = GetTaskLedgers(tval) 2178 print(FormatTaskLedgerSummary(ledgers)) 2179 return 2180 if "-J" in cmd_options: 2181 print_json = True 2182 2183 if cmd_args is None or len(cmd_args) == 0: 2184 raise ArgumentError("No arguments passed.") 2185 show_footprint_interval_max = False 2186 if "-I" in cmd_options: 2187 show_footprint_interval_max = True 2188 tval = addressof(kern.CreateValueFromAddress(cmd_args[0], 'task')) 2189 if not tval: 2190 raise ArgumentError("unknown arguments: %r" %cmd_args) 2191 ledgers = GetTaskLedgers(tval) 2192 if print_json: 2193 print(json.dumps(ledgers)) 2194 else: 2195 if (show_footprint_interval_max): 2196 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( 2197 "task [thread]", "entry", "#", "balance", "intrvl_max", "lifetime_max", "credit", 2198 "debit", "limit", "refill period", "lim pct", "warn pct", "over?", "flags")) 2199 else: 2200 print(FormatTaskLedgerSummary.header) 2201 print(FormatTaskLedgerSummary(ledgers, show_footprint_interval_max)) 2202 2203# EndMacro: showtaskledgers 2204 2205# Macro: showalltaskledgers 2206 2207@lldb_command('showalltaskledgers', "J") 2208def ShowAllTaskLedgers(cmd_args=None, cmd_options={}): 2209 """ Routine to print a summary of ledger entries for all tasks and respective threads 2210 Usage: showalltaskledgers [-J] 2211 -J : Output json 2212 """ 2213 print_json = False 2214 if "-J" in cmd_options: 2215 print_json = True 2216 tasks = [] 2217 for t in kern.tasks: 2218 task_val = unsigned(t) 2219 if not print_json: 2220 ShowTaskLedgers([task_val], cmd_options=cmd_options) 2221 else: 2222 tasks.append(GetTaskLedgers(t)) 2223 if print_json: 2224 print(json.dumps(tasks)) 2225 2226# EndMacro: showalltaskledgers 2227 2228# Macro: showprocuuidpolicytable 2229 2230@lldb_type_summary(['proc_uuid_policy_entry']) 2231@header("{0: <36s} {1: <10s}".format("uuid", "flags")) 2232def GetProcUUIDPolicyEntrySummary(entry): 2233 """ Summarizes the important fields in proc_uuid_policy_entry structure. 2234 params: entry: value - value object representing an entry 2235 returns: str - summary of the entry 2236 """ 2237 data = [] 2238 for i in range(16): 2239 data.append(int(entry.uuid[i])) 2240 flags = unsigned(entry.flags) 2241 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) 2242 return out_string 2243 2244@lldb_command('showprocuuidpolicytable') 2245def ShowProcUUIDPolicyTable(cmd_args=None): 2246 """ Routine to print the proc UUID policy table 2247 Usage: showprocuuidpolicytable 2248 """ 2249 hashslots = unsigned(kern.globals.proc_uuid_policy_hash_mask) 2250 print("{0: <8s} ".format("slot") + GetProcUUIDPolicyEntrySummary.header) 2251 for i in range(0, hashslots+1): 2252 headp = addressof(kern.globals.proc_uuid_policy_hashtbl[i]) 2253 entrynum = 0 2254 for entry in IterateListEntry(headp, 'entries'): 2255 print("{0: >2d}.{1: <5d} ".format(i, entrynum) + GetProcUUIDPolicyEntrySummary(entry)) 2256 entrynum += 1 2257 2258 2259# EndMacro: showprocuuidpolicytable 2260def build_fields_string(obj: value, fields: list[list[str]]) -> str: 2261 result = "" 2262 for field_name, human_name in fields: 2263 if (trp_field_value := get_field(obj, field_name)) is not None: 2264 result += f"{human_name}: {str(trp_field_value)} " 2265 2266 return result 2267 2268@lldb_command('showalltaskpolicy') 2269def ShowAllTaskPolicy(cmd_args=None): 2270 """ 2271 Routine to print a summary listing of all the tasks 2272 wq_state -> reports "number of workq threads", "number of scheduled workq threads", "number of pending work items" 2273 if "number of pending work items" seems stuck at non-zero, it may indicate that the workqueue mechanism is hung 2274 io_policy -> RAGE - rapid aging of vnodes requested 2275 NORM - normal I/O explicitly requested (this is the default) 2276 PASS - passive I/O requested (i.e. I/Os do not affect throttling decisions) 2277 THROT - throttled I/O requested (i.e. thread/task may be throttled after each I/O completes) 2278 """ 2279 global kern 2280 print(GetTaskSummary.header + " " + GetProcSummary.header) 2281 for t in kern.tasks: 2282 pval = GetProcFromTask(t) 2283 print(GetTaskSummary(t) +" "+ GetProcSummary(pval)) 2284 2285 requested_strings = [ 2286 ["trp_int_darwinbg", "DBG-int"], 2287 ["trp_ext_darwinbg", "DBG-ext"], 2288 ["trp_int_iotier", "iotier-int"], 2289 ["trp_ext_iotier", "iotier-ext"], 2290 ["trp_int_iopassive", "passive-int"], 2291 ["trp_ext_iopassive", "passive-ext"], 2292 ["trp_bg_iotier", "bg-iotier"], 2293 ["trp_terminated", "terminated"], 2294 # ["thrp_pidbind_bg", "bg-pidbind"], # no longer part of task requested policy 2295 ["trp_apptype", "apptype"], 2296 ["trp_boosted", "boosted"], 2297 ["trp_role", "role"], 2298 # ["trp_tal_enabled", "tal-enabled"], # tal_enabled is unused/deprecated 2299 ["trp_base_latency_qos", "latency-base"], 2300 ["trp_over_latency_qos", "latency-override"], 2301 ["trp_base_through_qos", "throughput-base"], 2302 ["trp_over_through_qos", "throughput-override"] 2303 ] 2304 2305 requested = build_fields_string(t.requested_policy, requested_strings) 2306 2307 suppression_strings = [ 2308 ["trp_sup_active", "active"], 2309 ["trp_sup_lowpri_cpu", "lowpri-cpu"], 2310 ["trp_sup_timer", "timer-throttling"], 2311 ["trp_sup_disk", "disk-throttling"], 2312 ["trp_sup_cpu_limit", "cpu-limits"], 2313 ["trp_sup_suspend", "suspend"], 2314 ["trp_sup_bg_sockets", "bg-sockets"] 2315 ] 2316 suppression = build_fields_string(t.requested_policy, suppression_strings) 2317 2318 effective_strings = [ 2319 ["tep_darwinbg", "background"], 2320 ["tep_lowpri_cpu", "lowpri-cpu"], 2321 ["tep_io_tier", "iotier"], 2322 ["tep_io_passive", "passive"], 2323 ["tep_all_sockets_bg", "bg-allsockets"], 2324 ["tep_new_sockets_bg", "bg-newsockets"], 2325 ["tep_bg_iotier", "bg-iotier"], 2326 ["tep_terminated", "terminated"], 2327 # ["t_gpu_deny", "gpu-deny"], # no longer exists 2328 ["tep_tal_engaged", "tal-engaged"], 2329 # ["t_suspended", "suspended"], # no longer exists 2330 ["tep_watchers_bg", "bg-watchers"], 2331 ["tep_latency_qos", "latency-qos"], 2332 ["tep_through_qos", "throughput-qos"], 2333 ["tep_sup_active", "suppression-active"], 2334 ["tep_role", "role"] 2335 ] 2336 effective = build_fields_string(t.effective_policy, effective_strings) 2337 2338 print("requested: " + requested) 2339 print("suppression: " + suppression) 2340 print("effective: " + effective) 2341 print("\n\n") 2342 2343 2344@lldb_command('showallsuspendedtasks', '') 2345def ShowSuspendedTasks(cmd_args=[], options={}): 2346 """ Show a list of suspended tasks with their process name summary. 2347 """ 2348 print(GetTaskSummary.header + ' ' + GetProcSummary.header) 2349 for t in kern.tasks: 2350 if t.suspend_count > 0: 2351 print(GetTaskSummary(t) + ' ' + GetProcSummary(GetProcFromTask(t))) 2352 return True 2353 2354# Macro: showallpte 2355@lldb_command('showallpte') 2356def ShowAllPte(cmd_args=None): 2357 """ Prints out the physical address of the pte for all tasks 2358 """ 2359 head_taskp = addressof(kern.globals.tasks) 2360 taskp = Cast(head_taskp.next, 'task *') 2361 while taskp != head_taskp: 2362 out_str = "task = {:#x} pte = {:#x}\t".format(taskp, taskp.map.pmap.ttep) 2363 procp = GetProcFromTask(taskp) 2364 if procp is not None: 2365 out_str += "{:s}\n".format(GetProcName(procp)) 2366 else: 2367 out_str += "\n" 2368 print(out_str) 2369 taskp = Cast(taskp.tasks.next, 'struct task *') 2370 2371# EndMacro: showallpte 2372 2373# Macro: showallrefcounts 2374@lldb_command('showallrefcounts') 2375@header("{0: <20s} {1: ^10s}".format("task", "ref_count")) 2376def ShowAllRefCounts(cmd_args=None): 2377 """ Prints the ref_count of all tasks 2378 """ 2379 out_str = '' 2380 head_taskp = addressof(kern.globals.tasks) 2381 taskp = Cast(head_taskp.next, 'task *') 2382 print(ShowAllRefCounts.header) 2383 while taskp != head_taskp: 2384 out_str += "{: <#20x}".format(taskp) 2385 out_str += "{: ^10d}\n".format(taskp.ref_count.ref_count) 2386 taskp = Cast(taskp.tasks.next, 'task *') 2387 print(out_str) 2388# EndMacro: showallrefcounts 2389 2390# Macro: showallrunnablethreads 2391@lldb_command('showallrunnablethreads', fancy=True) 2392def ShowAllRunnableThreads(cmd_args=None, cmd_options={}, O=None): 2393 """ Prints the sched usage information for all threads of each task 2394 """ 2395 out_str = '' 2396 for taskp in kern.tasks: 2397 for actp in IterateQueue(taskp.threads, 'thread *', 'task_threads'): 2398 if int(actp.state & 0x4): 2399 ShowActStack([unsigned(actp)], O=O) 2400 2401# EndMacro: showallrunnablethreads 2402 2403# Macro: showallschedusage 2404@lldb_command('showallschedusage') 2405@header("{0:<20s} {1:^10s} {2:^10s} {3:^15s}".format("Thread", "Priority", "State", "sched_usage")) 2406def ShowAllSchedUsage(cmd_args=None): 2407 """ Prints the sched usage information for all threads of each task 2408 """ 2409 out_str = '' 2410 for taskp in kern.tasks: 2411 ShowTask([str(unsigned(taskp))]) 2412 print(ShowAllSchedUsage.header) 2413 for actp in IterateQueue(taskp.threads, 'thread *', 'task_threads'): 2414 out_str = "{: <#20x}".format(actp) 2415 out_str += "{: ^10s}".format(str(int(actp.sched_pri))) 2416 state = int(actp.state) 2417 state_str = '' 2418 mask = 0x1 2419 while mask <= LAST_THREAD_STATE: 2420 state_str += THREAD_STATE_CHARS[int(state & mask)] 2421 mask <<= 1 2422 out_str += "{: ^10s}".format(state_str) 2423 out_str += "{: >15d}".format(actp.sched_usage) 2424 print(out_str + "\n") 2425 print("\n\n") 2426 2427# EndMacro: showallschedusage 2428 2429#Macro: showprocfilessummary 2430@lldb_command('showprocfilessummary') 2431@header("{0: <20s} {1: <20s} {2: >10s}".format("Process", "Name", "Number of Open Files")) 2432def ShowProcFilesSummary(cmd_args=None): 2433 """ Display the summary of open file descriptors for all processes in task list 2434 Usage: showprocfilessummary 2435 """ 2436 print(ShowProcFilesSummary.header) 2437 for proc in kern.procs: 2438 proc_filedesc = addressof(proc.p_fd) 2439 proc_ofiles = proc_filedesc.fd_ofiles 2440 proc_file_count = 0 2441 for fd in range(0, proc_filedesc.fd_afterlast): 2442 if unsigned(proc_ofiles[fd]) != 0: 2443 proc_file_count += 1 2444 print("{0: <#020x} {1: <32s} {2: >10d}".format(proc, GetProcName(proc), proc_file_count)) 2445 2446#EndMacro: showprocfilessummary 2447 2448@lldb_command('workinguserstacks') 2449def WorkingUserStacks(cmd_args=None): 2450 """ Print out the user stack for each thread in a task, followed by the user libraries. 2451 Syntax: (lldb) workinguserstacks <task_t> 2452 """ 2453 if cmd_args is None or len(cmd_args) == 0: 2454 raise ArgumentError() 2455 2456 task = kern.GetValueFromAddress(cmd_args[0], 'task *') 2457 print(GetTaskSummary.header + " " + GetProcSummary.header) 2458 pval = GetProcFromTask(task) 2459 print(GetTaskSummary(task) + " " + GetProcSummary(pval) + "\n \n") 2460 for thval in IterateQueue(task.threads, 'thread *', 'task_threads'): 2461 print("For thread 0x{0:x}".format(thval)) 2462 try: 2463 ShowThreadUserStack([hex(thval)]) 2464 except Exception as exc_err: 2465 print("Failed to show user stack for thread 0x{0:x}".format(thval)) 2466 if config['debug']: 2467 raise exc_err 2468 else: 2469 print("Enable debugging ('(lldb) xnudebug debug') to see detailed trace.") 2470 WorkingUserLibraries([hex(task)]) 2471 2472@static_var("exec_load_path", 0) 2473@lldb_command("workingkuserlibraries") 2474def WorkingUserLibraries(cmd_args=None): 2475 """ Show binary images known by dyld in target task 2476 For a given user task, inspect the dyld shared library state and print information about all Mach-O images. 2477 Syntax: (lldb)workinguserlibraries <task_t> 2478 """ 2479 if cmd_args is None or len(cmd_args) == 0: 2480 raise ArgumentError() 2481 2482 print("{0: <18s} {1: <12s} {2: <36s} {3: <50s}".format('address','type','uuid','path')) 2483 out_format = "0x{0:0>16x} {1: <12s} {2: <36s} {3: <50s}" 2484 task = kern.GetValueFromAddress(cmd_args[0], 'task_t') 2485 is_task_64 = int(task.t_flags) & 0x1 2486 dyld_all_image_infos_address = unsigned(task.all_image_info_addr) 2487 cur_data_offset = 0 2488 if dyld_all_image_infos_address == 0: 2489 print("No dyld shared library information available for task") 2490 return False 2491 vers_info_data = GetUserDataAsString(task, dyld_all_image_infos_address, 112) 2492 version = _ExtractDataFromString(vers_info_data, cur_data_offset, "uint32_t") 2493 cur_data_offset += 4 2494 if version > 12: 2495 print("Unknown dyld all_image_infos version number %d" % version) 2496 image_info_count = _ExtractDataFromString(vers_info_data, cur_data_offset, "uint32_t") 2497 WorkingUserLibraries.exec_load_path = 0 2498 if is_task_64: 2499 image_info_size = 24 2500 image_info_array_address = _ExtractDataFromString(vers_info_data, 8, "uint64_t") 2501 dyld_load_address = _ExtractDataFromString(vers_info_data, 8*4, "uint64_t") 2502 dyld_all_image_infos_address_from_struct = _ExtractDataFromString(vers_info_data, 8*13, "uint64_t") 2503 else: 2504 image_info_size = 12 2505 image_info_array_address = _ExtractDataFromString(vers_info_data, 4*2, "uint32_t") 2506 dyld_load_address = _ExtractDataFromString(vers_info_data, 4*5, "uint32_t") 2507 dyld_all_image_infos_address_from_struct = _ExtractDataFromString(vers_info_data, 4*14, "uint32_t") 2508 # Account for ASLR slide before dyld can fix the structure 2509 dyld_load_address = dyld_load_address + (dyld_all_image_infos_address - dyld_all_image_infos_address_from_struct) 2510 2511 i = 0 2512 while i < image_info_count: 2513 image_info_address = image_info_array_address + i * image_info_size 2514 img_data = GetUserDataAsString(task, image_info_address, image_info_size) 2515 if is_task_64: 2516 image_info_addr = _ExtractDataFromString(img_data, 0, "uint64_t") 2517 image_info_path = _ExtractDataFromString(img_data, 8, "uint64_t") 2518 else: 2519 image_info_addr = _ExtractDataFromString(img_data, 0, "uint32_t") 2520 image_info_path = _ExtractDataFromString(img_data, 4, "uint32_t") 2521 PrintImageInfo(task, image_info_addr, image_info_path) 2522 i += 1 2523 2524 # load_path might get set when the main executable is processed. 2525 if WorkingUserLibraries.exec_load_path != 0: 2526 PrintImageInfo(task, dyld_load_address, WorkingUserLibraries.exec_load_path) 2527 return 2528 2529# Macro: showstackaftertask 2530 2531@lldb_command('showstackaftertask', 'F:', fancy=True) 2532def Showstackaftertask(cmd_args=None, cmd_options={}, O=None): 2533 """ Routine to print the thread stacks for all tasks succeeding a given task 2534 Usage: showstackaftertask <0xaddress of task> 2535 or: showstackaftertask -F <taskname> 2536 """ 2537 if "-F" in cmd_options: 2538 # Find the task pointer corresponding to its task name 2539 find_task_str = cmd_options["-F"] 2540 task_list = FindTasksByName(find_task_str) 2541 2542 # Iterate through the list of tasks and print all task stacks thereafter 2543 for tval in task_list: 2544 ListTaskStacks(tval, O=O) 2545 return 2546 2547 if cmd_args is None or len(cmd_args) == 0: 2548 raise ArgumentError("Insufficient arguments") 2549 tval = kern.GetValueFromAddress(cmd_args[0], 'task *') 2550 if not tval: 2551 raise ArgumentError("unknown arguments: {:s}".format(str(cmd_args))) 2552 else: 2553 ListTaskStacks(tval, O=O) 2554 2555 ShowZombStacks(O=O) 2556 2557# EndMacro: showstackaftertask 2558 2559def ListTaskStacks(task, O=None): 2560 """ Search for a given task and print the list of all task stacks thereafter. 2561 """ 2562 # Initialize local variable task_flag to mark when a given task is found. 2563 task_flag=0 2564 2565 for t in kern.tasks: 2566 if (task_flag == 1): 2567 ShowTaskStacks(t, O=O) 2568 print("\n") 2569 if (t == task): 2570 task_flag = 1 2571 2572# Macro: showstackafterthread 2573@lldb_command('showstackafterthread', fancy=True) 2574def Showstackafterthread(cmd_args=None, cmd_options={}, O=None): 2575 """ Routine to print the stacks of all threads succeeding a given thread. 2576 Usage: Showstackafterthread <0xaddress of thread> 2577 """ 2578 # local variable thread_flag is used to mark when a given thread is found. 2579 thread_flag=0 2580 if cmd_args: 2581 threadval = kern.GetValueFromAddress(cmd_args[0], 'thread *') 2582 else: 2583 raise ArgumentError("No arguments passed") 2584 # Iterate through list of all tasks to look up a given thread 2585 for t in kern.tasks: 2586 if(thread_flag==1): 2587 pval = GetProcFromTask(t) 2588 print(GetTaskSummary.header + " "+ GetProcSummary.header) 2589 print(GetTaskSummary(t) + " "+ GetProcSummary(pval)) 2590 print("\n") 2591 # Look up for a given thread from the the list of threads of a given task 2592 for thval in IterateQueue(t.threads, 'thread *', 'task_threads'): 2593 if thread_flag == 1: 2594 print("\n") 2595 with O.table(GetThreadSummary.header, indent=True): 2596 print(GetThreadSummary(active_thread, O=O)) 2597 print(GetThreadBackTrace(thval, prefix="\t")+"\n") 2598 print("\n") 2599 2600 if thval == threadval: 2601 pval = GetProcFromTask(t) 2602 process_name = "{:s}".format(GetProcName(pval)) 2603 print("\n\n") 2604 print(" *** Continuing to dump the thread stacks from the process *** :" + " " + process_name) 2605 print("\n\n") 2606 thread_flag = 1 2607 print('\n') 2608 return 2609 2610