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