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