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