xref: /xnu-8019.80.24/tools/lldbmacros/workqueue.py (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1from xnu import *
2from process import GetBSDThread, GetMachThread
3from scheduler import GetRecentTimestamp
4import xnudefines
5
6def GetProcWorkqueue(proc):
7    wq = proc.p_wqptr;
8    if unsigned(wq):
9        return Cast(wq, "struct workqueue *");
10    return None
11
12@header("{:<20s} {:<20s} {:<20s} {:<10s} {:<10s} {:<10s} {:<10s} {:<10s} {:<10s} {:<30s}".format(
13    'task', 'proc', 'wq', 'sched', 'pending', 'idle', 'dying', 'creations', 'fulfilled', 'wq_flags'))
14def GetWorkqueueSummary(proc, wq):
15    wq_flags = []
16    if wq.wq_flags & GetEnumValue("workq_state_flags_t::WQ_EXITING"):
17        wq_flags.append("EXITING")
18    if wq.wq_flags & GetEnumValue("workq_state_flags_t::WQ_PROC_SUSPENDED"):
19        wq_flags.append("PROC_SUSPENDED")
20    if wq.wq_flags & GetEnumValue("workq_state_flags_t::WQ_DEATH_CALL_SCHEDULED"):
21        wq_flags.append("DEATH_CALL")
22
23    scheduled = GetEnumValue("workq_state_flags_t::WQ_DELAYED_CALL_SCHEDULED")
24    pended = GetEnumValue("workq_state_flags_t::WQ_DELAYED_CALL_PENDED")
25    if wq.wq_flags & (scheduled | pended):
26        s = "DELAYED_CALL["
27        if wq.wq_flags & scheduled: s += 'S'
28        if wq.wq_flags & pended: s += 'P'
29        s += ']'
30        wq_flags.append(s)
31
32    scheduled = GetEnumValue("workq_state_flags_t::WQ_IMMEDIATE_CALL_SCHEDULED")
33    pended = GetEnumValue("workq_state_flags_t::WQ_IMMEDIATE_CALL_PENDED")
34    if wq.wq_flags & (scheduled | pended):
35        s = "IMMEDIATE_CALL["
36        if wq.wq_flags & scheduled: s += 'S'
37        if wq.wq_flags & pended: s += 'P'
38        s += ']'
39        wq_flags.append(s)
40
41    return "{p.task: <#020x} {p: <#020x} {wq: <#020x} {wq.wq_threads_scheduled: <10d} {wq.wq_reqcount: <10d} {wq.wq_thidlecount: <10d} {wq.wq_thdying_count: <10d} {wq.wq_creations: <10d} {wq.wq_fulfilled: <10d} {wq_flags: <30s}".format(p=proc, wq=wq, wq_flags=" ".join(wq_flags));
42
43@header("{:<20s} {:<20s} {:>10s}  {:9s} {:<20s} {:<10s} {:<30s}".format(
44    'thread', 'uthread', 'thport', 'kind', 'kqueue', 'idle (ms)', 'uu_workq_flags'))
45def GetWQThreadSummary(th, uth):
46    p = th.t_tro.tro_proc
47    wq = p.p_wqptr
48
49    uu_workq_flags = []
50    if uth.uu_workq_flags & 0x01: uu_workq_flags.append("NEW")
51    if uth.uu_workq_flags & 0x02:
52        uu_workq_flags.append("RUNNING")
53        if wq.wq_creator == uth:
54            kind = "creator"
55        else:
56            kind = "workq"
57        idle = ""
58    else:
59        ts = kern.GetNanotimeFromAbstime(GetRecentTimestamp() - uth.uu_save.uus_workq_park_data.idle_stamp) / 1e9
60        kind = "idle"
61        idle = "%#.03f" % (ts)
62    if uth.uu_workq_flags & 0x04: uu_workq_flags.append("DYING")
63    if uth.uu_workq_flags & 0x08: uu_workq_flags.append("OVERCOMMIT")
64    if uth.uu_workq_flags & 0x100: uu_workq_flags.append("COOPERATIVE")
65    if uth.uu_workq_flags & 0x10: uu_workq_flags.append("OUTSIDE_QOS")
66    if uth.uu_workq_flags & 0x20: uu_workq_flags.append("IDLE_CLEANUP")
67    if uth.uu_workq_flags & 0x40: uu_workq_flags.append("EARLY_BOUND")
68    if uth.uu_workq_flags & 0x80: uu_workq_flags.append("CPU%")
69
70    kqr = uth.uu_kqr_bound
71    if not kqr:
72        kq = 0
73    elif kqr.tr_flags & 0x1: # kevent
74        kq = p.p_fd.fd_wqkqueue
75        kind = "kqwq[%s]" % (xnudefines.thread_qos_short_strings[int(kqr.tr_kq_qos_index)])
76    elif kqr.tr_flags & 0x2: # workloop
77        kq = ContainerOf(kqr, 'struct kqworkloop', 'kqwl_request')
78        kind = "workloop"
79    else:
80        kq = 0
81        kind = "???"
82
83    return "{th: <#020x} {uth: <#020x} {thport: >#010x}  {kind: <9s} {kq: <#020x} {idle: <10s} {uu_workq_flags: <30s}".format(th=th, uth=uth, thport=uth.uu_workq_thport, kind=kind, kq=kq, idle=idle, uu_workq_flags=" ".join(uu_workq_flags))
84
85@header("{:<20s} {:<20s} {:<20s} {:<10s} {:<4s} {:<6s} {:<6s} {:<6s} {:<30s}".format(
86    'request', 'kqueue', 'thread', 'state', '#', 'qos', 'kq_qos', 'kq_ovr', 'tr_flags'))
87def GetWorkqueueThreadRequestSummary(proc, req):
88    kq = 0
89    tr_flags = []
90
91    if req.tr_flags & 0x01:
92        tr_flags.append("KEVENT")
93        kq = proc.p_fd.fd_wqkqueue
94    if req.tr_flags & 0x02:
95        tr_flags.append("WORKLOOP")
96        kq = ContainerOf(req, 'struct kqworkloop', 'kqwl_request')
97    if req.tr_flags & 0x04: tr_flags.append("OVERCOMMIT")
98    if req.tr_flags & 0x08: tr_flags.append("PARAMS")
99    if req.tr_flags & 0x10: tr_flags.append("OUTSIDE_QOS")
100    if req.tr_flags & 0x20: tr_flags.append("COOPERATIVE")
101
102    state = {0: "IDLE", 1: "NEW", 2: "QUEUED", 3: "CANCELED", 4: "BINDING", 5: "BOUND" }[int(req.tr_state)]
103
104    thread = 0
105    if int(req.tr_state) in [4, 5]: # BINDING or BOUND
106        thread = req.tr_thread
107
108    qos = int(req.tr_qos)
109    if qos == 8:
110        qos = "MG"
111    elif qos == 7:
112        qos = "SP"
113    else:
114        qos = xnudefines.thread_qos_short_strings[qos]
115
116    kq_qos = xnudefines.thread_qos_short_strings[int(req.tr_kq_qos_index)]
117    kq_ovr = xnudefines.thread_qos_short_strings[int(req.tr_kq_override_index)]
118    req_addr = unsigned(addressof(req))
119
120    return "{req_addr: <#020x} {kq: <#020x} {thread: <#020x} {state: <10s} {req.tr_count: <4d} {qos: <6s} {kq_qos: <6s} {kq_ovr: <6s} {tr_flags: <30s}".format(
121            req_addr=req_addr, req=req, kq=kq, thread=thread, state=state, qos=qos, kq_qos=kq_qos, kq_ovr=kq_ovr, tr_flags=" ".join(tr_flags))
122
123@lldb_command('showwqthread', fancy=True)
124def ShowWQThread(cmd_args=None, cmd_options={}, O=None):
125    """ Shows info about a workqueue thread
126
127        usage: showworkqthread <thread_t>
128    """
129
130    if not cmd_args:
131        return O.error('missing struct proc * argument')
132
133    th = kern.GetValueFromAddress(cmd_args[0], "struct thread *")
134    if not (th.thread_tag & 0x20):
135        raise ArgumentError('not a workqueue thread')
136
137    with O.table(GetWQThreadSummary.header):
138        print GetWQThreadSummary(th, GetBSDThread(th))
139
140
141@lldb_command('showprocworkqueue', fancy=True)
142def ShowProcWorkqueue(cmd_args=None, cmd_options={}, O=None):
143    """ Shows the process workqueue
144
145        usage: showprocworkqueue <proc_t>
146    """
147
148    if not cmd_args:
149        return O.error('missing struct proc * argument')
150
151    proc = kern.GetValueFromAddress(cmd_args[0], "proc_t")
152    wq = Cast(proc.p_wqptr, "struct workqueue *");
153    if not wq:
154        return O.error("{:#x} doesn't have a workqueue", proc)
155
156    with O.table(GetWorkqueueSummary.header):
157        print GetWorkqueueSummary(proc, wq)
158
159        with O.table(GetWorkqueueThreadRequestSummary.header, indent=True):
160            if wq.wq_reqcount:
161                print ""
162            if wq.wq_event_manager_threadreq:
163                print GetWorkqueueThreadRequestSummary(proc, wq.wq_event_manager_threadreq)
164            for req in IterateSchedPriorityQueue(wq.wq_overcommit_queue, 'struct workq_threadreq_s', 'tr_entry'):
165                print GetWorkqueueThreadRequestSummary(proc, req)
166            for req in IterateSchedPriorityQueue(wq.wq_constrained_queue, 'struct workq_threadreq_s', 'tr_entry'):
167                print GetWorkqueueThreadRequestSummary(proc, req)
168            for req in IterateSchedPriorityQueue(wq.wq_special_queue, 'struct workq_threadreq_s', 'tr_entry'):
169                print GetWorkqueueThreadRequestSummary(proc, req)
170            for qos in xnudefines.thread_qos_short_strings:
171                bucket = 0;
172                if qos > 2: #Greater than BG
173                    bucket = qos - 2;
174                for req in IterateSTAILQ_HEAD(wq.wq_cooperative_queue[bucket], "tr_link"):
175                    print GetWorkqueueThreadRequestSummary(proc, req)
176
177
178        with O.table(GetWQThreadSummary.header, indent=True):
179            print ""
180            for uth in IterateTAILQ_HEAD(wq.wq_thrunlist, "uu_workq_entry"):
181                print GetWQThreadSummary(GetMachThread(uth), uth)
182            for uth in IterateTAILQ_HEAD(wq.wq_thidlelist, "uu_workq_entry"):
183                print GetWQThreadSummary(GetMachThread(uth), uth)
184            for uth in IterateTAILQ_HEAD(wq.wq_thnewlist, "uu_workq_entry"):
185                print GetWQThreadSummary(GetMachThread(uth), uth)
186
187@lldb_command('showallworkqueues', fancy=True)
188def ShowAllWorkqueues(cmd_args=None, cmd_options={}, O=None):
189    """ Display a summary of all the workqueues in the system
190
191        usage: showallworkqueues
192    """
193
194    with O.table(GetWorkqueueSummary.header):
195        for t in kern.tasks:
196            proc = Cast(t.bsd_info, 'proc *')
197            wq = Cast(proc.p_wqptr, "struct workqueue *");
198            if wq:
199                print GetWorkqueueSummary(proc, wq)
200