xref: /xnu-11215/tools/lldbmacros/ntstat.py (revision 8d741a5d)
1""" Please make sure you read the README COMPLETELY BEFORE reading anything below.
2    It is very critical that you read coding guidelines in Section E in README file.
3"""
4from xnu import *
5from utils import *
6from string import *
7from socket import *
8from enum import IntEnum
9
10import xnudefines
11from netdefines import *
12from routedefines import *
13
14######################################
15# Globals
16######################################
17""" Refer to in bsd/net/ntstat.c
18"""
19NSTAT_PROCDETAILS_MAGIC = 0xfeedc001
20NSTAT_GENERIC_SHADOW_MAGIC = 0xfadef00d
21TU_SHADOW_MAGIC = 0xfeedf00d
22""" Refer to nstat_provider_type_t in bsd/net/ntstat.h
23"""
24class NSTAT_PROVIDER(IntEnum):
25    NONE = 0
26    ROUTE = 1
27    TCP_KERNEL = 2
28    TCP_USERLAND = 3
29    UDP_KERNEL = 4
30    UDP_USERLAND = 5
31    IFNET = 6
32    SYSINFO = 7
33    QUIC_USERLAND = 8
34    CONN_USERLAND = 9
35    UDP_SUBFLOW = 10
36
37######################################
38# Helper functions
39######################################
40def ReverseIterateTAILQ_AnonymousHEAD(headval, field_name, element_type):
41    """ reverse iterate over a TAILQ_HEAD in kernel. refer to bsd/sys/queue.h
42    params:
43        headval         - value : value object representing the head of the list
44        field_name      - str   : string name of the field which holds the list links.
45        element_type    - str   : type of elements to be linked in the list
46    returns:
47        A generator does not return. It is used for iterating.
48        value : an object that is of type as headval->tqh_last. Always a pointer object
49    example usage:
50        list_head = kern.GetGlobalVariable('ctl_head')
51        for entry in ReverseIterateTAILQ_AnonymousHEAD(list_head, 'next', 'struct kctl *'):
52            print(entry)
53    """
54    head_first = headval.__getattr__('tqh_first')
55    head_first_addr = addressof(head_first)
56    iter_val = headval.__getattr__('tqh_last')
57    while (unsigned(iter_val) != unsigned(head_first_addr)) and (unsigned(iter_val) != 0) :
58        yield iter_val
59        element = Cast(iter_val, element_type)
60        iter_val = element.__getattr__(field_name).__getattr__('tqe_prev')
61    #end of yield loop
62
63def ShowNstatTUShadow(inshadow):
64    """ Display summary for an nstat_tu_shadow struct
65        params:
66            inshadow : cvalue object which points to 'struct nstat_tu_shadow *'
67    """
68    shad = Cast(inshadow, 'struct nstat_tu_shadow *')
69    procdetails = shad.shad_procdetails
70    out_string = ""
71    if shad :
72        format_string = "nstat_tu_shadow {0: <#0x}: next={1: <#020x} prev={2: <#020x} context (necp_client *)={3: <#020x} live={4: <d}"
73        out_string += format_string.format(shad, shad.shad_link.tqe_next, shad.shad_link.tqe_prev, shad.shad_provider_context, shad.shad_live)
74
75        magic = unsigned(shad.shad_magic)
76        if (magic != TU_SHADOW_MAGIC) :
77            format_string = " INVALID shad magic {0: <#0x}"
78            out_string += format_string.format(magic)
79
80        if (procdetails) :
81            format_string = "  --> procdetails {0: <#0x}: pid={1: <d} name={2: <s} refcnt={3: <d}"
82            out_string += format_string.format(procdetails, procdetails.pdet_pid, procdetails.pdet_procname, procdetails.pdet_refcnt)
83
84            procmagic = unsigned(procdetails.pdet_magic)
85            if (procmagic != NSTAT_PROCDETAILS_MAGIC) :
86                format_string = " INVALID proc magic {0: <#0x}"
87                out_string += format_string.format(procmagic)
88
89    print(out_string)
90
91def ShowNstatGShadow(inshadow):
92    """ Display summary for an nstat_generic_shadow
93        params:
94            inshadow : cvalue object which points to 'struct nstat_generic_shadow *'
95    """
96    gshad = Cast(inshadow, 'struct nstat_generic_shadow *')
97    procdetails = gshad.gshad_procdetails
98    out_string = ""
99    if gshad :
100        prov_string = GetNstatProviderString(gshad.gshad_provider)
101        format_string = "nstat_generic_shadow {0: <#0x}: prov={1: <8s} next={2: <#020x} prev={3: <#020x} refcnt={4: <d} "
102        out_string += format_string.format(gshad, prov_string, gshad.gshad_link.tqe_next, gshad.gshad_link.tqe_prev, gshad.gshad_refcnt)
103
104        ## context
105        if (gshad.gshad_provider == NSTAT_PROVIDER.CONN_USERLAND) :
106            out_string += "context (necp_client *)={0: <#020x} ".format(gshad.gshad_provider_context)
107        elif (gshad.gshad_provider == NSTAT_PROVIDER.UDP_SUBFLOW) :
108            out_string += "context (soflow_hash_entry *)={0: <#020x} ".format(gshad.gshad_provider_context)
109
110        magic = unsigned(gshad.gshad_magic)
111        if (magic != NSTAT_GENERIC_SHADOW_MAGIC) :
112            format_string = " INVALID gshad magic {0: <#0x}"
113            out_string += format_string.format(magic)
114
115        if (procdetails) :
116            format_string = " --> procdetails {0: <#0x}: pid={1: <d} name={2: <s} refcnt={3: <d}"
117            out_string += format_string.format(procdetails, procdetails.pdet_pid, procdetails.pdet_procname, procdetails.pdet_refcnt)
118
119            procmagic = unsigned(procdetails.pdet_magic)
120            if (procmagic != NSTAT_PROCDETAILS_MAGIC) :
121                format_string = " INVALID proc magic {0: <#0x}"
122                out_string += format_string.format(procmagic)
123
124    print(out_string)
125
126def GetNstatProcdetailsBrief(procdetails):
127    """ Display a brief summary for an nstat_procdetails struct
128        params:
129            procdetails : cvalue object which points to 'struct nstat_procdetails *'
130        returns:
131            str : A string describing various information for the nstat_procdetails structure
132    """
133    procdetails = Cast(procdetails, 'struct nstat_procdetails *')
134    out_string = ""
135    if (procdetails) :
136        format_string = " --> pid={0: <d} name={1: <s} refcnt={2: <d}"
137        out_string += format_string.format(procdetails.pdet_pid, procdetails.pdet_procname, procdetails.pdet_refcnt)
138
139        procmagic = unsigned(procdetails.pdet_magic)
140        if (procmagic != NSTAT_PROCDETAILS_MAGIC) :
141            format_string = " INVALID proc magic {0: <#0x}"
142            out_string += format_string.format(procmagic)
143
144    return out_string
145
146def ShowNstatProcdetails(procdetails):
147    """ Display a summary for an nstat_procdetails struct
148        params:
149            procdetails : cvalue object which points to 'struct nstat_procdetails *'
150    """
151    procdetails = Cast(procdetails, 'struct nstat_procdetails *')
152    out_string = ""
153    if (procdetails) :
154        format_string = "nstat_procdetails: {0: <#020x} next={1: <#020x} prev={2: <#020x} "
155        out_string += format_string.format(procdetails, procdetails.pdet_link.tqe_next, procdetails.pdet_link.tqe_prev)
156        out_string += GetNstatProcdetailsBrief(procdetails)
157
158    print(out_string)
159
160def GetNstatTUShadowBrief(shadow):
161    """ Display a summary for an nstat_tu_shadow struct
162        params:
163            shadow : cvalue object which points to 'struct nstat_tu_shadow *'
164        returns:
165            str : A string describing various information for the nstat_tu_shadow structure
166    """
167    out_string = ""
168    shad = Cast(shadow, 'struct nstat_tu_shadow *')
169    procdetails = shad.shad_procdetails
170    procdetails = Cast(procdetails, 'struct nstat_procdetails *')
171    out_string = ""
172    if shad :
173        format_string = " shadow {0: <#0x}: necp_client={1: <#020x} live={2: <d}"
174        out_string += format_string.format(shad, shad.shad_provider_context, shad.shad_live)
175        magic = unsigned(shad.shad_magic)
176        if (magic != TU_SHADOW_MAGIC) :
177            format_string = " INVALID shad magic {0: <#0x}"
178            out_string += format_string.format(magic)
179        elif (procdetails) :
180            out_string += GetNstatProcdetailsBrief(procdetails)
181
182    return out_string
183
184def GetNstatGenericShadowBrief(shadow):
185    """ Display a summary for an nstat_generic_shadow struct
186        params:
187            shadow : cvalue object which points to 'struct nstat_generic_shadow *'
188        returns:
189            str : A string describing various information for the nstat_tu_shadow structure
190    """
191    gshad = Cast(shadow, 'struct nstat_generic_shadow *')
192    procdetails = gshad.gshad_procdetails
193    procdetails = Cast(procdetails, 'struct nstat_procdetails *')
194    out_string = ""
195    if gshad :
196        format_string = " gshadow {0: <#0x}:"
197        out_string += format_string.format(gshad)
198        if (gshad.gshad_provider == NSTAT_PROVIDER.CONN_USERLAND) :
199            out_string += "necp_client={0: <#020x} ".format(gshad.gshad_provider_context)
200        elif (gshad.gshad_provider == NSTAT_PROVIDER.UDP_SUBFLOW) :
201            out_string += "soflow_hash_entry={0: <#020x} ".format(gshad.gshad_provider_context)
202        else :
203            out_string += "context {0: <#020x} ".format(gshad.gshad_provider_context)
204        out_string += " refcnt={0: <d} ".format(gshad.gshad_refcnt)
205
206        magic = unsigned(gshad.gshad_magic)
207        if (magic != NSTAT_GENERIC_SHADOW_MAGIC) :
208            format_string = " INVALID gshad magic {0: <#0x}"
209            out_string += format_string.format(magic)
210        elif (procdetails) :
211            out_string += GetNstatProcdetailsBrief(procdetails)
212
213    return out_string
214
215def GetNstatTUCookieBrief(cookie):
216    """ Display a summary for an nstat_tucookie struct
217        params:
218            shadow : cvalue object which points to 'struct nstat_tucookie *'
219        returns:
220            str : A string describing various information for the nstat_tucookie structure
221    """
222    out_string = ""
223    tucookie = Cast(cookie, 'struct nstat_tucookie *')
224    inp = tucookie.inp
225    pname = tucookie.pname
226    inpcb = Cast(inp, 'struct inpcb *')
227    inp_socket = inpcb.inp_socket
228    sock = Cast(inp_socket, 'struct socket *')
229    format_string = " inpcb={0: <#0x}: socket={1: <#020x} process={2: <s}"
230    out_string += format_string.format(inpcb, sock, pname)
231    return out_string
232
233def GetNstatProviderString(provider):
234    providers = {
235        NSTAT_PROVIDER.NONE: "none",
236        NSTAT_PROVIDER.ROUTE: "route",
237        NSTAT_PROVIDER.TCP_KERNEL: "TCP k",
238        NSTAT_PROVIDER.TCP_USERLAND: "TCP u",
239        NSTAT_PROVIDER.UDP_KERNEL: "UDP k",
240        NSTAT_PROVIDER.UDP_USERLAND: "UDP u",
241        NSTAT_PROVIDER.IFNET: "ifnet",
242        NSTAT_PROVIDER.SYSINFO: "sysinfo",
243        NSTAT_PROVIDER.QUIC_USERLAND: "quic u",
244        NSTAT_PROVIDER.CONN_USERLAND: "conn u",
245        NSTAT_PROVIDER.UDP_SUBFLOW: "subflow",
246    }
247    return providers.get(unsigned(provider), "unknown")
248
249def ShowNstatSrc(insrc):
250    """ Display summary for an nstat_src struct
251        params:
252            insrc : cvalue object which points to 'struct nstat_src *'
253    """
254    src = Cast(insrc, 'nstat_src *')
255    prov = src.nts_provider
256    prov = Cast(prov, 'nstat_provider *')
257    prov_string = GetNstatProviderString(prov.nstat_provider_id)
258    out_string = ""
259    if src :
260        format_string = "  nstat_src {0: <#0x}: prov={1: <8s} next={2: <#020x} prev={3: <#020x} srcref={4: <d} seq={5: <d}"
261        out_string += format_string.format(src, prov_string, src.nts_client_link.tqe_next, src.nts_client_link.tqe_prev, src.nts_srcref, src.nts_seq)
262
263        if ((prov.nstat_provider_id == NSTAT_PROVIDER.TCP_USERLAND) or
264            (prov.nstat_provider_id == NSTAT_PROVIDER.UDP_USERLAND) or
265            (prov.nstat_provider_id == NSTAT_PROVIDER.QUIC_USERLAND)) :
266            out_string += GetNstatTUShadowBrief(src.nts_cookie);
267        elif ((prov.nstat_provider_id == NSTAT_PROVIDER.CONN_USERLAND) or
268            (prov.nstat_provider_id == NSTAT_PROVIDER.UDP_SUBFLOW)) :
269            out_string += GetNstatGenericShadowBrief(src.nts_cookie);
270        elif ((prov.nstat_provider_id == NSTAT_PROVIDER.TCP_KERNEL) or
271            (prov.nstat_provider_id == NSTAT_PROVIDER.UDP_KERNEL)) :
272            out_string += GetNstatTUCookieBrief(src.nts_cookie);
273
274    print(out_string)
275
276def ShowNstatClient(inclient, reverse):
277    """ Display an nstat_client struct
278        params:
279            client : value object representing an nstat_client in the kernel
280    """
281    client = Cast(inclient, 'nstat_client *')
282    out_string = ""
283    if client :
284        format_string = "nstat_client {0: <#0x}: next={1: <#020x} src-head={2: <#020x} tail={3: <#020x}"
285        out_string += format_string.format(client, client.ntc_next, client.ntc_src_queue.tqh_first, client.ntc_src_queue.tqh_last)
286        procdetails = client.ntc_procdetails
287        if (procdetails) :
288            format_string = "  --> procdetails {0: <#0x}: pid={1: <d} name={2: <s} refcnt={3: <d}"
289            out_string += format_string.format(procdetails, procdetails.pdet_pid, procdetails.pdet_procname, procdetails.pdet_refcnt)
290
291    print(out_string)
292    if reverse:
293         print("\nreverse nstat_src list:\n")
294         iterator = ReverseIterateTAILQ_AnonymousHEAD(client.ntc_src_queue, 'nts_client_link', 'struct nstat_src *')
295    else:
296         print("\nreverse nstat_src list:\n")
297         iterator = IterateTAILQ_HEAD(client.ntc_src_queue, 'nts_client_link')
298    for src in iterator:
299        ShowNstatSrc(src)
300
301######################################
302# Print functions
303######################################
304def PrintNstatClientList(reverse):
305    print("nstat_clients list:\n")
306    client = kern.globals.nstat_clients
307    client = cast(client, 'nstat_client *')
308    while client != 0:
309        ShowNstatClient(client, reverse)
310        client = cast(client.ntc_next, 'nstat_client *')
311
312def PrintNstatProcdetailList(reverse):
313    procdetails_head = kern.globals.nstat_procdetails_head
314    if reverse:
315        print("\nreverse nstat_procdetails list:\n")
316        iterator = ReverseIterateTAILQ_AnonymousHEAD(procdetails_head, 'pdet_link', 'struct nstat_procdetails *')
317    else:
318        print("\nnstat_procdetails list:\n")
319        iterator = IterateTAILQ_HEAD(procdetails_head, 'pdet_link')
320    for procdetails in iterator:
321        ShowNstatProcdetails(procdetails)
322
323def PrintNstatGenericShadowList(reverse):
324    gshadows = kern.globals.nstat_gshad_head
325    if reverse:
326        print("\nreverse nstat_ghsad list:\n")
327        iterator = ReverseIterateTAILQ_AnonymousHEAD(gshadows, 'gshad_link', 'struct nstat_generic_shadow *')
328    else:
329        print("\nnstat_ghsad list:\n")
330        iterator = IterateTAILQ_HEAD(gshadows, 'gshad_link')
331    for gshad in iterator:
332        ShowNstatGShadow(gshad)
333
334def PrintNstatTUShadowList(reverse):
335    shadows = kern.globals.nstat_userprot_shad_head
336    if reverse:
337        print("\nreverse nstat_userprot_shad list:\n")
338        iterator = ReverseIterateTAILQ_AnonymousHEAD(shadows, 'shad_link', 'struct nstat_tu_shadow *')
339    else:
340        print("\nnstat_userprot_shad list:\n")
341        iterator = IterateTAILQ_HEAD(shadows, 'shad_link')
342    for shad in iterator:
343        ShowNstatTUShadow(shad)
344
345######################################
346# LLDB commands
347######################################
348# Macro: showallntstat
349
350@lldb_command('showallntstat', 'R')
351def ShowAllNtstat(cmd_args=None, cmd_options={}) :
352    """ Show the contents of various ntstat (network statistics) data structures
353
354        usage: showallntstat [-R]
355            -R  : print ntstat list in reverse
356    """
357    reverse = '-R' in cmd_options
358
359    PrintNstatClientList(reverse)
360    PrintNstatTUShadowList(reverse)
361    PrintNstatGenericShadowList(reverse)
362    PrintNstatProcdetailList(reverse)
363
364# EndMacro: showallntstat
365