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