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