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