xref: /xnu-12377.61.12/tools/lldbmacros/mbufs.py (revision 4d495c6e23c53686cf65f45067f79024cf5dcee8)
1
2""" Please make sure you read the README COMPLETELY BEFORE reading anything below.
3    It is very critical that you read coding guidelines in Section E in README file.
4"""
5from xnu import *
6from utils import *
7
8from mbufdefines import *
9import xnudefines
10import kmemory
11
12def MbufZoneByName(name):
13    for i in range(1, int(kern.GetGlobalVariable('num_zones'))):
14        z = addressof(kern.globals.zone_array[i])
15        zs = addressof(kern.globals.zone_security_array[i])
16        if ZoneName(z, zs) == name:
17            return (z, zs)
18    return None
19
20# Macro: mbuf_stat
21@lldb_command('mbuf_stat')
22def MBufStat(cmd_args=None):
23    """ Print extended mbuf allocator statistics.
24    """
25    hdr_format = "{0: <16s} {1: >8s} {2: >8s} {3: ^16s} {4: >8s} {5: >12s} {6: >8s} {7: >8s} {8: >8s} {9: >8s}"
26    print(hdr_format.format('class', 'total', 'cached', 'uncached', 'inuse', 'failed', 'waiter', 'notified', 'purge', 'max'))
27    print(hdr_format.format('name', 'objs', 'objs', 'objs/slabs', 'objs', 'alloc count', 'count', 'count', 'count', 'objs'))
28    print(hdr_format.format('-'*16, '-'*8, '-'*8, '-'*16, '-'*8, '-'*12, '-'*8, '-'*8, '-'*8, '-'*8))
29    entry_format = "{0: <16s} {1: >8d} {2: >8d} {3:>7d} / {4:<6d} {5: >8d} {6: >12d} {7: >8d} {8: >8d} {9: >8d} {10: >8d}"
30    num_items = sizeof(kern.globals.mbuf_table) // sizeof(kern.globals.mbuf_table[0])
31    ncpus = int(kern.globals.ncpu)
32    for i in range(num_items):
33        mbuf = kern.globals.mbuf_table[i]
34        mcs = Cast(mbuf.mtbl_stats, 'mb_class_stat_t *')
35        if kern.arch != 'x86_64':
36            cname = str(mcs.mbcl_cname)
37            if cname == "mbuf":
38                zone = MbufZoneByName("mbuf")
39            elif cname == "cl":
40                zone = MbufZoneByName("data.mbuf.cluster.2k")
41            elif cname == "bigcl":
42                zone = MbufZoneByName("data.mbuf.cluster.4k")
43            elif cname == "16kcl":
44                zone = MbufZoneByName("data.mbuf.cluster.16k")
45            elif cname == "mbuf_cl":
46                zone = MbufZoneByName("mbuf.composite.2k")
47            elif cname == "mbuf_bigcl":
48                zone = MbufZoneByName("mbuf.composite.4k")
49            elif cname == "mbuf_16kcl":
50                zone = MbufZoneByName("mbuf.composite.16k")
51            zone_stats = GetZone(zone[0], zone[1], [], [])
52            print(entry_format.format(cname,
53                                      int(zone_stats['size'] / mcs.mbcl_size),
54                                      zone_stats['cache_element_count'],
55                                      zone_stats['free_element_count'],
56                                      0,
57                                      int(zone_stats['used_size'] / mcs.mbcl_size),
58                                      zone_stats['alloc_fail_count'],
59                                      0, 0, 0,
60                                      mbuf.mtbl_maxlimit))
61
62        else:
63            mc = mbuf.mtbl_cache
64            total = 0
65            total += int(mc.mc_full.bl_total) * int(mc.mc_cpu[0].cc_bktsize)
66            ccp_arr = mc.mc_cpu
67            for i in range(ncpus):
68                ccp = ccp_arr[i]
69                if int(ccp.cc_objs) > 0:
70                    total += int(ccp.cc_objs)
71                if int(ccp.cc_pobjs) > 0:
72                    total += int(ccp.cc_pobjs)
73            print(entry_format.format(mcs.mbcl_cname, mcs.mbcl_total,  total,
74                                      mcs.mbcl_infree, mcs.mbcl_slab_cnt,
75                                      (mcs.mbcl_total - total - mcs.mbcl_infree),
76                                      mcs.mbcl_fail_cnt, mbuf.mtbl_cache.mc_waiter_cnt,
77                                      mcs.mbcl_notified, mcs.mbcl_purge_cnt,
78                                      mbuf.mtbl_maxlimit))
79# EndMacro: mbuf_stat
80
81def DumpMbufData(mp, count):
82    if kern.arch == 'x86_64':
83        mdata = mp.m_hdr.mh_data
84        mlen = mp.m_hdr.mh_len
85        flags = mp.m_hdr.mh_flags
86        if flags & M_EXT:
87            mdata = mp.M_dat.MH.MH_dat.MH_ext.ext_buf
88    else:
89        mdata = mp.M_hdr_common.M_hdr.mh_data
90        mlen = mp.M_hdr_common.M_hdr.mh_len
91        flags = mp.M_hdr_common.M_hdr.mh_flags
92        if flags & M_EXT:
93            mdata = mp.M_hdr_common.M_ext.ext_buf
94    if (count > mlen):
95        count = mlen
96    cmd = "memory read -force -size 1 -count {0:d} 0x{1:x}".format(count, mdata)
97    print(lldb_run_command(cmd))
98
99def DecodeMbufData(in_mp, decode_as="ether"):
100    import scapy.all
101    err = lldb.SBError()
102    scan = in_mp
103    while (scan):
104        full_buf = b''
105        mp = scan
106        while (mp):
107            if kern.arch == 'x86_64':
108                mdata = mp.m_hdr.mh_data
109                mlen = unsigned(mp.m_hdr.mh_len)
110                flags = mp.m_hdr.mh_flags
111                if flags & M_EXT:
112                    mdata = mp.M_dat.MH.MH_dat.MH_ext.ext_buf
113                mnext = mp.m_hdr.mh_next
114            else:
115                mdata = mp.M_hdr_common.M_hdr.mh_data
116                mlen = unsigned(mp.M_hdr_common.M_hdr.mh_len)
117                flags = mp.M_hdr_common.M_hdr.mh_flags
118                if flags & M_EXT:
119                    mdata = mp.M_hdr_common.M_ext.ext_buf
120                mnext = mp.M_hdr_common.M_hdr.mh_next
121
122            addr = mdata.GetSBValue().GetValueAsAddress()
123            buf = LazyTarget.GetProcess().ReadMemory(addr, mlen, err)
124            full_buf += buf
125            if mnext == 0:
126                try:
127                    if decode_as == "ether":
128                        pkt = scapy.layers.l2.Ether(full_buf)
129                    elif decode_as == "ip":
130                        pkt = scapy.layers.inet.IP(full_buf)
131                    elif decode_as == "ip6":
132                        pkt = scapy.layers.inet6.IPv6(full_buf)
133                    elif decode_as == "tcp":
134                        pkt = scapy.layers.inet.TCP(full_buf)
135                    elif decode_as == "udp":
136                        pkt = scapy.layers.inet.UDP(full_buf)
137                    else:
138                        print("invalid decoder" + decode_as)
139                        return
140                    pkt.show()
141                    break
142                except KeyboardInterrupt:
143                    raise KeyboardInterrupt
144                except:
145                    break
146            mp = mnext
147        if kern.arch == 'x86_64':
148            scan = scan.m_hdr.mh_nextpkt
149        else:
150            scan = scan.M_hdr_common.M_hdr.mh_nextpkt
151
152
153# Macro: mbuf_decode
154@lldb_command('mbuf_decode', '')
155def MbufDecode(cmd_args=None, cmd_options={}):
156    """Decode an mbuf using scapy.
157        Usage: mbuf_decode <mbuf address>
158    """
159    if cmd_args is None or len(cmd_args) == 0:
160        print("usage: mbuf_decode <address> [decode_as]")
161        return
162    mp = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
163    if len(cmd_args) > 1:
164        decode_as = cmd_args[1]
165        DecodeMbufData(mp, decode_as)
166    else:
167        DecodeMbufData(mp)
168# EndMacro: mbuf_decode
169
170# Macro: mbuf_dumpdata
171@lldb_command('mbuf_dumpdata', 'C:')
172def MbufDumpData(cmd_args=None, cmd_options={}):
173    """Dump the mbuf data
174        Usage: mbuf_dumpdata  <mbuf address> [-C <count>]
175    """
176    if cmd_args is None or len(cmd_args) == 0:
177        raise ArgumentError()
178
179    mp = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
180    if kern.arch == 'x86_64':
181        mdata = mp.m_hdr.mh_data
182        mhlen = mp.m_hdr.mh_len
183    else:
184        mdata = mp.M_hdr_common.M_hdr.mh_data
185        mhlen = mp.M_hdr_common.M_hdr.mh_len
186    mlen = 0
187    if "-C" in cmd_options:
188        mlen = ArgumentStringToInt(cmd_options["-C"])
189        if (mlen > mhlen):
190            mlen = mhlen
191    else:
192        mlen = mhlen
193    DumpMbufData(mp, mlen)
194# EndMacro: mbuf_dumpdata
195
196def ShowMbuf(prefix, mp, count, total, dump_data_len):
197    out_string = ""
198    mca = ""
199    if kern.arch == 'x86_64':
200        mhlen = mp.m_hdr.mh_len
201        mhtype = mp.m_hdr.mh_type
202        mhflags = mp.m_hdr.mh_flags
203        mhdata = mp.m_hdr.mh_data
204        if mhflags & M_EXT:
205            extbuf = mp.M_dat.MH.MH_dat.MH_ext.ext_buf
206        if kern.globals.mclaudit != 0:
207            mca += GetMbufBuf2Mca(mp) + ", "
208    else:
209        mhlen = mp.M_hdr_common.M_hdr.mh_len
210        mhtype = mp.M_hdr_common.M_hdr.mh_type
211        mhflags = mp.M_hdr_common.M_hdr.mh_flags
212        mhdata = mp.M_hdr_common.M_hdr.mh_data
213        if mhflags & M_EXT:
214            extbuf = mp.M_hdr_common.M_ext.ext_buf
215    mbuf_walk_format = "{0:s}{1:d} 0x{2:x} [len {3:d}, type {4:d}, data 0x{5:x}, "
216    out_string += mbuf_walk_format.format(prefix, count[0], mp, mhlen, mhtype, mhdata)
217    out_string += "flags " + GetMbufFlagsAsString(mhflags) + ", "
218    if (mhflags & M_PKTHDR):
219        out_string += GetMbufPktCrumbs(mp) + ", "
220    if (mhflags & M_EXT):
221        m_ext_format = "ext_buf 0x{0:x}, "
222        out_string += m_ext_format.format(extbuf)
223    if (mca != ""):
224        out_string += mca + ", "
225    total[0] = total[0] + mhlen
226    out_string += "total " + str(total[0]) + "]"
227    print(out_string)
228    if (dump_data_len > 0):
229        DumpMbufData(mp, dump_data_len)
230
231def WalkMufNext(prefix, mp, count, total, dump_data_len):
232    remaining_len = dump_data_len
233    while (mp):
234        if kern.arch == 'x86_64':
235            mhlen = mp.m_hdr.mh_len
236            mhnext = mp.m_hdr.mh_next
237        else:
238            mhlen = mp.M_hdr_common.M_hdr.mh_len
239            mhnext = mp.M_hdr_common.M_hdr.mh_next
240        count[0] += 1
241        ShowMbuf(prefix, mp, count, total, remaining_len)
242        if (remaining_len > mhlen):
243            remaining_len -= mhlen
244        else:
245            remaining_len = 0
246        mp = mhnext
247
248# Macro: mbuf_walkpkt
249@lldb_command('mbuf_walkpkt', 'C:')
250def MbufWalkPacket(cmd_args=None, cmd_options={}):
251    """ Walk the mbuf packet chain (m_nextpkt)
252        Usage: mbuf_walkpkt  <mbuf address> [-C <count>]
253    """
254    if cmd_args is None or len(cmd_args) == 0:
255        raise ArgumentError("Missing argument 0 in user function.")
256    mp = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
257
258    dump_data_len = 0
259    if "-C" in cmd_options:
260        dump_data_len = ArgumentStringToInt(cmd_options["-C"])
261
262    count_packet = 0
263    count_mbuf = 0
264    total_len = 0
265
266    while (mp):
267        count_packet += 1
268        prefix = "{0:d}.".format(count_packet)
269        count = [0]
270        total = [0]
271        WalkMufNext(prefix, mp, count, total, dump_data_len)
272        count_mbuf += count[0]
273        total_len += total[0]
274        if kern.arch == 'x86_64':
275            mp = mp.m_hdr.mh_nextpkt
276        else:
277            mp = mp.M_hdr_common.M_hdr.mh_nextpkt
278    out_string = "Total packets: {0:d} mbufs: {1:d} length: {2:d} ".format(count_packet, count_mbuf, total_len)
279    print(out_string)
280# EndMacro: mbuf_walkpkt
281
282# Macro: mbuf_walk
283@lldb_command('mbuf_walk', 'C:')
284def MbufWalk(cmd_args=None, cmd_options={}):
285    """ Walk the mbuf chain (m_next)
286        Usage: mbuf_walk  <mbuf address> [-C <count>]
287    """
288    if cmd_args is None or len(cmd_args) == 0:
289        raise ArgumentError("Missing argument 0 in user function.")
290    mp = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
291
292    dump_data_len = 0
293    if "-C" in cmd_options:
294        dump_data_len = ArgumentStringToInt(cmd_options["-C"])
295
296    count = [0]
297    total = [0]
298    prefix = ""
299    WalkMufNext(prefix, mp, count, total, dump_data_len)
300# EndMacro: mbuf_walk
301
302# Macro: mbuf_buf2slab
303@lldb_command('mbuf_buf2slab')
304def MbufBuf2Slab(cmd_args=None):
305    """ Given an mbuf object, find its corresponding slab address
306    """
307    if cmd_args is None or len(cmd_args) == 0:
308        raise ArgumentError("Missing argument 0 in user function.")
309
310    if kern.arch != 'x86_64':
311        print("mcache is disabled, use kasan whatis")
312        return
313
314    m = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
315    slab = GetMbufSlab(m)
316    if (kern.ptrsize == 8):
317        mbuf_slab_format = "0x{0:<16x}"
318        print(mbuf_slab_format.format(slab))
319    else:
320        mbuf_slab_format = "0x{0:<8x}"
321        print(mbuf_slab_format.format(slab))
322# EndMacro: mbuf_buf2slab
323
324# Macro: mbuf_buf2mca
325@lldb_command('mbuf_buf2mca')
326def MbufBuf2Mca(cmd_args=None):
327    """ Find the mcache audit structure of the corresponding mbuf
328    """
329    if kern.arch != 'x86_64':
330        print("mcache is disabled, use kasan whatis")
331        return
332
333    m = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
334    print(GetMbufBuf2Mca(m))
335    return
336# EndMacro: mbuf_buf2mca
337
338# Macro: mbuf_slabs
339@lldb_command('mbuf_slabs')
340def MbufSlabs(cmd_args=None):
341    """ Print all slabs in the group
342    """
343
344    if kern.arch != 'x86_64':
345        print("mcache is disabled, use kasan whatis or zprint")
346        return
347
348    out_string = ""
349    if cmd_args is None or len(cmd_args) == 0:
350        raise ArgumentError("Invalid arguments passed.")
351
352    slg = kern.GetValueFromAddress(cmd_args[0], 'mcl_slabg_t *')
353    x = 0
354
355    if (kern.ptrsize == 8):
356        slabs_string_format = "{0:>4d}: 0x{1:16x} 0x{2:16x} 0x{3:016x} 0x{4:016x} {5:10d} {6:3d} {7:3d} {8:3d} {9:5d} {10:>6s} "
357        out_string += "slot  slab               next               obj                mca                tstamp     C   R   N   size   flags\n"
358        out_string += "----- ------------------ ------------------ ------------------ ------------------ ---------- --- --- --- ------ -----\n"
359    else:
360        slabs_string_format = "{0:>4d}: 0x{1:8x} 0x{2:8x} 0x{3:08x} 0x{4:08x} {5:10d} {6:3d} {7:3d} {8:3d} {9:5d} {10:>6s} "
361        out_string += "slot  slab       next       obj        mca        tstamp     C   R   N   size   flags\n"
362        out_string += "----- ---------- ---------- ---------- ---------- ---------- --- --- --- ------ -----\n"
363
364    mbutl = cast(kern.globals.mbutl, 'unsigned char *')
365    nslabspmb = int((1 << MBSHIFT) >> unsigned(kern.globals.page_shift))
366    while x < nslabspmb:
367        sl = addressof(slg.slg_slab[x])
368        mca = 0
369        obj = sl.sl_base
370        ts = 0
371
372        if (kern.globals.mclaudit != 0 and obj != 0):
373            mca = GetMbufMcaPtr(obj, sl.sl_class)
374            trn = (mca.mca_next_trn + unsigned(kern.globals.mca_trn_max) - 1) % unsigned(kern.globals.mca_trn_max)
375            ts = mca.mca_trns[trn].mca_tstamp
376
377        out_string += slabs_string_format.format((x + 1), sl, sl.sl_next, obj, mca, int(ts), int(sl.sl_class), int(sl.sl_refcnt), int(sl.sl_chunks), int(sl.sl_len), hex(sl.sl_flags))
378
379        if (sl.sl_flags != 0):
380            out_string += "<"
381            if sl.sl_flags & SLF_MAPPED:
382                out_string += "mapped"
383            if sl.sl_flags & SLF_PARTIAL:
384                out_string += ",partial"
385            if sl.sl_flags & SLF_DETACHED:
386                out_string += ",detached"
387            out_string += ">"
388        out_string += "\n"
389
390        if sl.sl_chunks > 1:
391            z = 1
392            c = sl.sl_len // sl.sl_chunks
393
394            while z < sl.sl_chunks:
395                obj = sl.sl_base + (c * z)
396                mca = 0
397                ts = 0
398
399                if (kern.globals.mclaudit != 0 ):
400                    mca = GetMbufMcaPtr(obj, sl.sl_class)
401                    trn = (mca.mca_next_trn + unsigned(kern.globals.mca_trn_max) - 1) % unsigned(kern.globals.mca_trn_max)
402                    ts = mca.mca_trns[trn].mca_tstamp
403
404                if (kern.ptrsize == 8):
405                    chunk_string_format = "                                            0x{0:16x} 0x{1:16x} {2:10d}\n"
406                else:
407                    chunk_string_format = "                            0x{0:8x} {1:4s} {2:10d}\n"
408
409                out_string += chunk_string_format.format(int(obj), int(mca), int(ts))
410
411                z += 1
412        x += 1
413    print(out_string)
414# EndMacro: mbuf_slabs
415
416# Macro: mbuf_slabstbl
417@lldb_command('mbuf_slabstbl')
418def MbufSlabsTbl(cmd_args=None):
419    """ Print slabs table
420    """
421    out_string = ""
422    x = 0
423
424    if kern.arch != 'x86_64':
425        print("mcache is disabled, use kasan whatis or zprint")
426        return
427
428    if (kern.ptrsize == 8):
429        out_string += "slot slabg              slabs range\n"
430        out_string += "---- ------------------ -------------------------------------------\n"
431    else:
432        out_string += "slot slabg      slabs range\n"
433        out_string += "---- ---------- ---------------------------\n"
434
435    slabstbl = kern.globals.slabstbl
436    slabs_table_blank_string_format = "{0:>3d}: - \n"
437    nslabspmb = int(((1 << MBSHIFT) >> unsigned(kern.globals.page_shift)))
438    while (x < unsigned(kern.globals.maxslabgrp)):
439        slg = slabstbl[x]
440        if (slg == 0):
441            out_string += slabs_table_blank_string_format.format(x+1)
442        else:
443            if (kern.ptrsize == 8):
444                slabs_table_string_format = "{0:>3d}: 0x{1:16x}  [ 0x{2:16x} - 0x{3:16x} ]\n"
445                out_string += slabs_table_string_format.format(x+1, slg, addressof(slg.slg_slab[0]), addressof(slg.slg_slab[nslabspmb-1]))
446            else:
447                slabs_table_string_format = "{0:>3d}: 0x{1:8x}  [ 0x{2:8x} - 0x{3:8x} ]\n"
448                out_string += slabs_table_string_format.format(x+1, slg, addressof(slg.slg_slab[0]), addressof(slg.slg_slab[nslabspmb-1]))
449
450        x += 1
451    print(out_string)
452# EndMacro: mbuf_slabstbl
453
454def MbufDecode(mbuf, decode_pkt):
455    # Ignore free'd mbufs.
456    if kern.arch == 'x86_64':
457        mhlen = mbuf.m_hdr.mh_len
458        mhtype = mbuf.m_hdr.mh_type
459        mhflags = mbuf.m_hdr.mh_flags
460    else:
461        mhlen = mbuf.M_hdr_common.M_hdr.mh_len
462        mhtype = mbuf.M_hdr_common.M_hdr.mh_type
463        mhflags = mbuf.M_hdr_common.M_hdr.mh_flags
464    if mhtype == 0:
465        return
466    flags = int(mhflags)
467    length = int(mhlen)
468    if length < 20 or length > 8 * 1024:
469        # Likely not a packet.
470        return
471    out_string = "mbuf found @ 0x{0:x}, length {1:d}, {2:s}, {3:s}".format(mbuf, length, GetMbufFlags(mbuf), GetMbufPktCrumbs(mbuf))
472    print(out_string)
473    if flags & M_PKTHDR:
474        if kern.arch == 'x86_64':
475            rcvif = mbuf.M_dat.MH.MH_pkthdr.rcvif
476        else:
477            rcvif = mbuf.M_hdr_common.M_pkthdr.rcvif
478        if rcvif != 0:
479            try:
480                print("receive interface " + rcvif.if_xname)
481            except ValueError:
482                pass
483    if decode_pkt:
484        DecodeMbufData(mbuf)
485
486
487# Macro: mbuf_walk_slabs
488@lldb_command('mbuf_walk_slabs')
489def MbufWalkSlabs(cmd_args=None):
490    """
491    Walks the mbuf slabs table backwards and tries to detect and decode mbufs.
492    Use 'mbuf_walk_slabs decode' to decode the mbuf using scapy.
493    """
494    decode_pkt = False
495    if len(cmd_args) > 0 and cmd_args[0] == 'decode':
496        decode_pkt = True
497
498    if kern.arch != 'x86_64':
499        for mbuf in kmemory.Zone("mbuf").iter_allocated(gettype("mbuf")):
500            MbufDecode(value(mbuf.AddressOf()), decode_pkt)
501        return
502
503    slabstbl = kern.globals.slabstbl
504    nslabspmb = int(((1 << MBSHIFT) >> unsigned(kern.globals.page_shift)))
505    mbutl = cast(kern.globals.mbutl, 'unsigned char *')
506    x = unsigned(kern.globals.maxslabgrp)
507    while x >= 0:
508        slg = slabstbl[x]
509        if (slg == 0):
510            x -= 1
511            continue
512        j = 0
513        while j < nslabspmb:
514            sl = addressof(slg.slg_slab[j])
515            obj = sl.sl_base
516            # Ignore slabs with a single chunk
517            # since that's unlikely to contain an mbuf
518            # (more likely a cluster).
519            if sl.sl_chunks > 1:
520                z = 0
521                c = sl.sl_len // sl.sl_chunks
522
523                while z < sl.sl_chunks:
524                    obj = kern.GetValueFromAddress(sl.sl_base + c * z)
525                    mbuf = cast(obj, 'struct mbuf *')
526                    MbufDecode(mbuf, decode_pkt)
527                    z += 1
528            j += 1
529        x -= 1
530
531# EndMacro: mbuf_walk_slabs
532
533def GetMbufMcaPtr(m, cl):
534    pgshift = int(kern.globals.page_shift)
535    ix = int((m - Cast(kern.globals.mbutl, 'char *')) >> pgshift)
536    page_addr = (Cast(kern.globals.mbutl, 'char *') + (ix << pgshift))
537
538
539    if (int(cl) == 0):
540        midx = int((m - page_addr) >> 8)
541        mca = kern.globals.mclaudit[ix].cl_audit[midx]
542    elif (int(cl) == 1):
543        midx = int((m - page_addr) >> 11)
544        mca = kern.globals.mclaudit[ix].cl_audit[midx]
545    elif (int(cl) == 2):
546        midx = int((m - page_addr) >> 12)
547        mca = kern.globals.mclaudit[ix].cl_audit[midx]
548    else:
549        mca = kern.globals.mclaudit[ix].cl_audit[0]
550    return Cast(mca, 'mcache_audit_t *')
551
552def GetMbufSlab(m):
553    pgshift = int(kern.globals.page_shift)
554    gix = int((Cast(m, 'char *') - Cast(kern.globals.mbutl, 'char *')) >> MBSHIFT)
555    slabstbl = kern.globals.slabstbl
556    ix = int((Cast(m, 'char *') - Cast(slabstbl[gix].slg_slab[0].sl_base, 'char *')) >> pgshift)
557    return addressof(slabstbl[gix].slg_slab[ix])
558
559def GetMbufBuf2Mca(m):
560    sl = GetMbufSlab(m)
561    mca = GetMbufMcaPtr(m, sl.sl_class)
562    return str(mca)
563
564def GetMbufWalkAllSlabs(show_a, show_f, show_tr):
565    out_string = ""
566
567    kern.globals.slabstbl[0]
568
569    x = 0
570    total = 0
571    total_a = 0
572    total_f = 0
573
574    if (show_a and not(show_f)):
575        out_string += "Searching only for active... \n"
576    if (not(show_a) and show_f):
577        out_string += "Searching only for inactive... \n"
578    if (show_a and show_f):
579        out_string += "Displaying all... \n"
580
581    if (kern.ptrsize == 8):
582        show_mca_string_format = "{0:>4s} {1:>4s} {2:>16s} {3:>16s} {4:>16} {5:>12s} {6:12s}"
583        out_string += show_mca_string_format.format("slot", "idx", "slab address", "mca address", "obj address", "type", "allocation state\n")
584    else:
585        show_mca_string_format = "{0:4s} {1:4s} {2:8s} {3:8s} {4:8} {5:12s} {6:12s}"
586        out_string += show_mca_string_format.format("slot", "idx", "slab address", "mca address", "obj address", "type", "allocation state\n")
587
588    nslabspmb = unsigned((1 << MBSHIFT) >> unsigned(kern.globals.page_shift))
589    while (x < unsigned(kern.globals.slabgrp)):
590        slg = kern.globals.slabstbl[x]
591        y = 0
592        while (y < nslabspmb):
593            sl = addressof(slg.slg_slab[y])
594            base = sl.sl_base
595            if (base == 0):
596                break
597
598            mca = GetMbufMcaPtr(base, sl.sl_class)
599            first = 1
600
601            while ((Cast(mca, 'int') != 0) and (unsigned(mca.mca_addr) != 0)):
602                printmca = 0
603                if (mca.mca_uflags & (MB_INUSE | MB_COMP_INUSE)):
604                    total_a = total_a + 1
605                    printmca = show_a
606                    if (show_tr > 2) and (mca.mca_uflags & MB_SCVALID) == 0:
607                        printmca = 0
608                else:
609                    total_f = total_f + 1
610                    printmca = show_f
611
612                if (printmca != 0):
613                    if (first == 1):
614                        if (kern.ptrsize == 8):
615                            mca_string_format = "{0:4d} {1:4d} 0x{2:16x} "
616                            out_string += mca_string_format.format(x, y, sl)
617                        else:
618                            mca_string_format = "{0:4d} {1:4d} 0x{02:8x} "
619                            out_string += mca_string_format.format(x, y, sl)
620                    else:
621                        if (kern.ptrsize == 8):
622                            out_string += "                             "
623                        else:
624                            out_string += "                     "
625
626                    if (kern.ptrsize == 8):
627                        mca_string_format = "0x{0:16x} 0x{1:16x}"
628                        out_string += mca_string_format.format(mca, mca.mca_addr)
629                    else:
630                        mca_string_format = "0x{0:8x} 0x{1:8x}"
631                        out_string += mca_string_format.format(mca, mca.mca_addr)
632
633                    out_string += GetMbufMcaCtype(mca, 0)
634
635                    if (mca.mca_uflags & (MB_INUSE | MB_COMP_INUSE)):
636                        out_string += "active        "
637                    else:
638                        out_string += "       freed "
639                    if (show_tr > 1) and (mca.mca_uflags & MB_SCVALID):
640                        m = Cast(mca.mca_addr, 'struct mbuf *')
641                        mbuf_string = GetMbufFlags(m)
642                        mbuf_string += " " + GetMbufPktCrumbs(m)
643                        if (mbuf_string != ""):
644                            if (kern.ptrsize == 8):
645                                out_string += "\n                              " + mbuf_string
646                            else:
647                                out_string += "\n                      " + mbuf_string
648                    if (first == 1):
649                        first = 0
650
651                    out_string += "\n"
652                    total = total + 1
653
654                    if (show_tr != 0):
655                        if (mca.mca_next_trn == 0):
656                            trn = 1
657                        else:
658                            trn = 0
659                        out_string += "Transaction " + str(int(trn)) + " at " + str(int(mca.mca_trns[int(trn)].mca_tstamp)) + " by thread: 0x" + str(hex(mca.mca_trns[int(trn)].mca_thread)) + ":\n"
660                        cnt = 0
661                        while (cnt < mca.mca_trns[int(trn)].mca_depth):
662                            kgm_pc = mca.mca_trns[int(trn)].mca_stack[int(cnt)]
663                            out_string += str(int(cnt) + 1) + " "
664                            out_string += GetPc(kgm_pc)
665                            cnt += 1
666
667                    print(out_string)
668                    out_string = ""
669                mca = mca.mca_next
670
671            y += 1
672
673        x += 1
674
675    if (total and show_a and show_f):
676        out_string += "total objects = " + str(int(total)) + "\n"
677        out_string += "active/unfreed objects = " + str(int(total_a)) + "\n"
678        out_string += "freed/in_cache objects = " + str(int(total_f)) + "\n"
679
680    return out_string
681
682def GetMbufFlagsAsString(mbuf_flags):
683    flags = (unsigned)(mbuf_flags & 0xff)
684    out_string = ""
685    i = 0
686    num = 1
687    while num <= flags:
688        if flags & num:
689            out_string += mbuf_flags_strings[i] + ","
690        i += 1
691        num = num << 1
692    return out_string.rstrip(",")
693
694def GetMbufFlags(m):
695    out_string = ""
696    if (m != 0):
697        if kern.arch == 'x86_64':
698            mhflags = m.m_hdr.mh_flags
699        else:
700            mhflags = m.M_hdr_common.M_hdr.mh_flags
701        out_string += "m_flags: " + hex(mhflags)
702        if (mhflags != 0):
703             out_string += " " + GetMbufFlagsAsString(mhflags)
704    return out_string
705
706MBUF_TYPES = [None] * 32
707MBUF_TYPES[0] = "MT_FREE"
708MBUF_TYPES[1] = "MT_DATA"
709MBUF_TYPES[2] = "MT_HEADER"
710MBUF_TYPES[3] = "MT_SOCKET"
711MBUF_TYPES[4] = "MT_PCB"
712MBUF_TYPES[5] = "MT_RTABLE"
713MBUF_TYPES[6] = "MT_HTABLE"
714MBUF_TYPES[7] = "MT_ATABLE"
715MBUF_TYPES[8] = "MT_SONAME"
716# 9 not used
717MBUF_TYPES[10] = "MT_SOOPTS"
718MBUF_TYPES[11] = "MT_FTABLE"
719MBUF_TYPES[12] = "MT_RIGHTS"
720MBUF_TYPES[13] = "MT_IFADDR"
721MBUF_TYPES[14] = "MT_CONTROL"
722MBUF_TYPES[15] = "MT_OOBDATA"
723MBUF_TYPES[16] = "MT_TAG"
724
725def GetMbufType(m):
726    out_string = ""
727    if (m != 0):
728        if kern.arch == 'x86_64':
729            mhtype = m.m_hdr.mh_type
730        else:
731            mhtype = m.M_hdr_common.M_hdr.mh_type
732        out_string += "type: " + MBUF_TYPES[mhtype]
733    return out_string
734
735# Macro: mbuf_show_m_flags
736@lldb_command('mbuf_show_m_flags')
737def MbufShowFlags(cmd_args=None):
738    """ Return a formatted string description of the mbuf flags
739    """
740    if cmd_args is None or len(cmd_args) == 0:
741        raise ArgumentError()
742
743    m = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
744    print(GetMbufFlags(m))
745
746def GetMbufPktCrumbsAsString(mbuf_crumbs):
747    flags = (unsigned)(mbuf_crumbs & 0xffff)
748    out_string = ""
749    i = 0
750    num = 1
751    while num <= flags:
752        if flags & num:
753            out_string += mbuf_pkt_crumb_strings[i] + ","
754        i += 1
755        num = num << 1
756    return out_string.rstrip(",")
757
758def GetMbufPktCrumbs(m):
759    out_string = ""
760    if (m != 0):
761        if kern.arch == 'x86_64':
762            mhflags = m.m_hdr.mh_flags
763        else:
764            mhflags = m.M_hdr_common.M_hdr.mh_flags
765        if (mhflags & M_PKTHDR) != 0:
766            if kern.arch == 'x86_64':
767                pktcrumbs = m.M_dat.MH.MH_pkthdr.pkt_crumbs
768            else:
769                pktcrumbs = m.M_hdr_common.M_pkthdr.pkt_crumbs
770            out_string += "pkt_crumbs: 0x{0:x}".format(pktcrumbs)
771            if (pktcrumbs != 0):
772                out_string += " " + GetMbufPktCrumbsAsString(pktcrumbs)
773    return out_string
774
775# Macro: mbuf_showpktcrumbs
776@lldb_command('mbuf_showpktcrumbs')
777def MbufShowPktCrumbs(cmd_args=None):
778    """ Print the packet crumbs of an mbuf object mca
779    """
780    if cmd_args is None or len(cmd_args) == 0:
781        raise ArgumentError()
782
783    m = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
784    print(GetMbufPktCrumbs(m))
785
786def GetMbufMcaCtype(mca, vopt):
787    cp = mca.mca_cache
788    mca_class = unsigned(cp.mc_private)
789    csize = unsigned(kern.globals.mbuf_table[mca_class].mtbl_stats.mbcl_size)
790    done = 0
791    out_string = "    "
792    if (csize == MSIZE):
793        if (vopt):
794            out_string += "M (mbuf) "
795        else:
796            out_string += "M     "
797        return out_string
798    if (csize == MCLBYTES):
799        if (vopt):
800            out_string += "CL (2K cluster) "
801        else:
802            out_string += "CL     "
803        return out_string
804    if (csize == MBIGCLBYTES):
805        if (vopt):
806            out_string += "BCL (4K cluster) "
807        else:
808            out_string += "BCL     "
809        return out_string
810    if (csize == M16KCLBYTES):
811        if (vopt):
812            out_string += "JCL (16K cluster) "
813        else:
814            out_string += "JCL     "
815        return out_string
816
817    if (csize == (MSIZE + MCLBYTES)):
818        if (mca.mca_uflags & MB_SCVALID):
819            if (mca.mca_uptr):
820                out_string += "M+CL  "
821                if vopt:
822                    out_string += "(paired mbuf, 2K cluster) "
823            else:
824                out_string += "M-CL  "
825                if vopt:
826                    out_string += "(unpaired mbuf, 2K cluster) "
827        else:
828            if (mca.mca_uptr):
829                out_string += "CL+M  "
830                if vopt:
831                    out_string += "(paired 2K cluster, mbuf) "
832            else:
833                out_string += "CL-M  "
834                if vopt:
835                    out_string += "(unpaired 2K cluster, mbuf) "
836        return out_string
837
838    if (csize == (MSIZE + MBIGCLBYTES)):
839        if (mca.mca_uflags & MB_SCVALID):
840            if (mca.mca_uptr):
841                out_string += "M+BCL  "
842                if vopt:
843                    out_string += "(paired mbuf, 4K cluster) "
844            else:
845                out_string += "M-BCL  "
846                if vopt:
847                    out_string += "(unpaired mbuf, 4K cluster) "
848        else:
849            if (mca.mca_uptr):
850                out_string += "BCL+M  "
851                if vopt:
852                    out_string += "(paired 4K cluster, mbuf) "
853            else:
854                out_string += "BCL-m  "
855                if vopt:
856                    out_string += "(unpaired 4K cluster, mbuf) "
857        return out_string
858
859    if (csize == (MSIZE + M16KCLBYTES)):
860        if (mca.mca_uflags & MB_SCVALID):
861            if (mca.mca_uptr):
862                out_string += "M+BCL  "
863                if vopt:
864                    out_string += "(paired mbuf, 4K cluster) "
865            else:
866                out_string += "M-BCL  "
867                if vopt:
868                    out_string += "(unpaired mbuf, 4K cluster) "
869        else:
870            if (mca.mca_uptr):
871                out_string += "BCL+M  "
872                if vopt:
873                    out_string += "(paired 4K cluster, mbuf) "
874            else:
875                out_string += "BCL-m  "
876                if vopt:
877                    out_string += "(unpaired 4K cluster, mbuf) "
878        return out_string
879
880    out_string += "unknown: " + cp.mc_name
881    return out_string
882
883
884def GetPointerAsString(kgm_pc):
885    if (kern.ptrsize == 8):
886        pointer_format_string = "0x{0:<16x} "
887    else:
888        pointer_format_string = "0x{0:<8x} "
889    return pointer_format_string.format(kgm_pc)
890
891def GetPc(kgm_pc):
892    out_string = GetSourceInformationForAddress(unsigned(kgm_pc)) + "\n"
893    return out_string
894
895
896def GetMbufWalkZone(show_a, show_f, show_tr):
897    out_string = ""
898    total = 0
899    total_a = 0
900    total_f = 0
901    if (show_a and not(show_f)):
902        out_string += "Searching only for active... \n"
903    if (not(show_a) and show_f):
904        out_string += "Searching only for inactive... \n"
905    if (show_a and show_f):
906        out_string += "Displaying all... \n"
907    f = "{0:>18s} {1:s}\n"
908    out_string += f.format("address", "type flags and crumbs")
909    print(out_string)
910    if show_a:
911        for mbuf_sbv in kmemory.Zone("mbuf").iter_allocated(gettype("mbuf")):
912            mbuf = value(mbuf_sbv.AddressOf())
913            total_a += 1
914            total += 1
915            mbuf_string = GetMbufFlags(mbuf)
916            mbuf_string += " " + GetMbufType(mbuf)
917            mbuf_string += " " + GetMbufPktCrumbs(mbuf)
918            if mbuf_string != "":
919                out_string = f.format(hex(mbuf), mbuf_string)
920            print(out_string)
921            if show_tr:
922                print(lldb_run_command('kasan whatis {addr}'.format(addr=hex(mbuf))))
923    if show_f:
924        for mbuf_sbv in kmemory.Zone("mbuf").iter_free(gettype("mbuf")):
925            mbuf = value(mbuf_sbv.AddressOf())
926            total_f += 1
927            total += 1
928            mbuf_string = GetMbufFlags(mbuf)
929            mbuf_string += " " + GetMbufType(mbuf)
930            mbuf_string += " " + GetMbufPktCrumbs(mbuf)
931            if mbuf_string != "":
932                out_string = f.format(hex(mbuf), mbuf_string)
933            print(out_string)
934            if show_tr:
935                print(lldb_run_command('kasan whatis {addr}'.format(addr=hex(mbuf))))
936
937    if total and show_a and show_f:
938        out_string += "total objects = " + str(int(total)) + "\n"
939        out_string += "active/unfreed objects = " + str(int(total_a)) + "\n"
940        out_string += "freed/in_cache objects = " + str(int(total_f)) + "\n"
941        print(out_string)
942
943
944# Macro: mbuf_showactive
945@lldb_command('mbuf_showactive')
946def MbufShowActive(cmd_args=None):
947    """ Print all active/in-use mbuf objects
948        Pass 1 to show the most recent transaction stack trace
949        Pass 2 to also display the mbuf flags and packet crumbs
950        Pass 3 to limit display to mbuf and skip clusters
951    """
952    if kern.arch != 'x86_64':
953        if cmd_args:
954            GetMbufWalkZone(1, 0, ArgumentStringToInt(cmd_args[0]))
955        else:
956            GetMbufWalkZone(1, 0, 0)
957    else:
958        if cmd_args:
959            print(GetMbufWalkAllSlabs(1, 0, ArgumentStringToInt(cmd_args[0])))
960        else:
961            print(GetMbufWalkAllSlabs(1, 0, 0))
962# EndMacro: mbuf_showactive
963
964
965# Macro: mbuf_showinactive
966@lldb_command('mbuf_showinactive')
967def MbufShowInactive(cmd_args=None):
968    """ Print all freed/in-cache mbuf objects
969    """
970    if kern.arch != 'x86_64':
971        GetMbufWalkZone(0, 1, 0)
972    else:
973        print(GetMbufWalkAllSlabs(0, 1, 0))
974# EndMacro: mbuf_showinactive
975
976# Macro: mbuf_show_type_summary
977@lldb_command('mbuf_show_type_summary')
978def MbufShowTypeSummary(cmd_args=None):
979    """
980    Print types of all allocated mbufs.
981    Only supported on Apple Silicon.
982    """
983    types = [0] * 32
984    for mbuf_sbv in kmemory.Zone("mbuf").iter_allocated(gettype("mbuf")):
985        mbuf = value(mbuf_sbv.AddressOf())
986        mhtype = mbuf.M_hdr_common.M_hdr.mh_type
987        types[mhtype] += 1
988    for mbuf_sbv in kmemory.Zone("mbuf").iter_free(gettype("mbuf")):
989        mbuf = value(mbuf_sbv.AddressOf())
990        mhtype = mbuf.M_hdr_common.M_hdr.mh_type
991        types[mhtype] += 1
992
993    print("mbuf types allocated and in the caches:")
994    for t in range(len(MBUF_TYPES)):
995        if types[t] != 0:
996            print(MBUF_TYPES[t], types[t])
997
998# EndMacro: mbuf_show_type_summary
999
1000
1001# Macro: mbuf_showmca
1002@lldb_command('mbuf_showmca')
1003def MbufShowMca(cmd_args=None):
1004    """ Print the contents of an mbuf mcache audit structure
1005    """
1006    if kern.arch != 'x86_64':
1007        print("mcache is disabled, use kasan whatis or zstack_findelem")
1008        return
1009    out_string = ""
1010    pgshift = unsigned(kern.globals.page_shift)
1011    if cmd_args:
1012        mca = kern.GetValueFromAddress(cmd_args[0], 'mcache_audit_t *')
1013        cp = mca.mca_cache
1014        out_string += "object type:\t"
1015        out_string += GetMbufMcaCtype(mca, 1)
1016        out_string += "\nControlling mcache :\t" + hex(mca.mca_cache) + " (" + str(cp.mc_name) + ")\n"
1017        if (mca.mca_uflags & MB_INUSE):
1018            out_string += " inuse"
1019        if (mca.mca_uflags & MB_COMP_INUSE):
1020            out_string += " comp_inuse"
1021        if (mca.mca_uflags & MB_SCVALID):
1022            out_string += " scvalid"
1023        out_string += "\n"
1024        if (mca.mca_uflags & MB_SCVALID):
1025            mbutl = Cast(kern.globals.mbutl, 'unsigned char *')
1026            ix = (mca.mca_addr - mbutl) >> pgshift
1027            clbase = mbutl + (ix << pgshift)
1028            mclidx = (mca.mca_addr - clbase) >> 8
1029            out_string += "mbuf obj :\t\t" + hex(mca.mca_addr) + "\n"
1030            out_string += "mbuf index :\t\t" + str(mclidx + 1) + " (out of 16) in cluster base " + hex(clbase) + "\n"
1031            if (int(mca.mca_uptr) != 0):
1032                peer_mca = cast(mca.mca_uptr, 'mcache_audit_t *')
1033                out_string += "paired cluster obj :\t" + hex(peer_mca.mca_addr) + " (mca " + hex(peer_mca) + ")\n"
1034            out_string += "saved contents :\t" + hex(mca.mca_contents) + " (" + str(int(mca.mca_contents_size)) + " bytes)\n"
1035        else:
1036            out_string += "cluster obj :\t\t" + hex(mca.mca_addr) + "\n"
1037            if (mca.mca_uptr != 0):
1038                peer_mca = cast(mca.mca_uptr, 'mcache_audit_t *')
1039                out_string += "paired mbuf obj :\t" + hex(peer_mca.mca_addr) + " (mca " + hex(peer_mca) + ")\n"
1040
1041        for idx in range(unsigned(kern.globals.mca_trn_max), 0, -1):
1042                trn = (mca.mca_next_trn + idx - 1) % unsigned(kern.globals.mca_trn_max)
1043                out_string += "transaction {:d} (tstamp {:d}, thread 0x{:x}):\n".format(trn, mca.mca_trns[trn].mca_tstamp, mca.mca_trns[trn].mca_thread)
1044                cnt = 0
1045                while (cnt < mca.mca_trns[trn].mca_depth):
1046                    kgm_pc = mca.mca_trns[trn].mca_stack[cnt]
1047                    out_string += "  " + str(cnt + 1) + ".  "
1048                    out_string += GetPc(kgm_pc)
1049                    cnt += 1
1050
1051        msc = cast(mca.mca_contents, 'mcl_saved_contents_t *')
1052        msa = addressof(msc.sc_scratch)
1053        if (mca.mca_uflags & MB_SCVALID):
1054            if (msa.msa_depth > 0):
1055                out_string += "Recent scratch transaction (tstamp {:d}, thread 0x{:x}):\n".format(msa.msa_tstamp, msa.msa_thread)
1056                cnt = 0
1057                while (cnt < msa.msa_depth):
1058                    kgm_pc = msa.msa_stack[cnt]
1059                    out_string += "  " + str(cnt + 1) + ".  "
1060                    out_string += GetPc(kgm_pc)
1061                    cnt += 1
1062
1063            if (msa.msa_pdepth > 0):
1064                out_string += "previous scratch transaction (tstamp {:d}, thread 0x{:x}):\n".format(msa.msa_ptstamp, msa.msa_pthread)
1065        if (msa):
1066            cnt = 0
1067            while (cnt < msa.msa_pdepth):
1068                kgm_pc = msa.msa_pstack[cnt]
1069                out_string += "  " + str(cnt + 1) + ".  "
1070                out_string += GetPc(kgm_pc)
1071                cnt += 1
1072    else:
1073        out_string += "Missing argument 0 in user function."
1074
1075    print(out_string)
1076# EndMacro: mbuf_showmca
1077
1078
1079# Macro: mbuf_showall
1080@lldb_command('mbuf_showall')
1081def MbufShowAll(cmd_args=None):
1082    """ Print all mbuf objects
1083    """
1084    if kern.arch != 'x86_64':
1085        GetMbufWalkZone(1, 1, 1)
1086    else:
1087        print(GetMbufWalkAllSlabs(1, 1, 1))
1088# EndMacro: mbuf_showall
1089
1090# Macro: mbuf_countchain
1091@lldb_command('mbuf_countchain')
1092def MbufCountChain(cmd_args=None):
1093    """ Count the length of an mbuf chain
1094    """
1095    if cmd_args is None or len(cmd_args) == 0:
1096        raise ArgumentError("Missing argument 0 in user function.")
1097
1098    mp = kern.GetValueFromAddress(cmd_args[0], 'mbuf *')
1099
1100    pkt = 0
1101    nxt = 0
1102
1103    while (mp):
1104        pkt = pkt + 1
1105        if kern.arch == 'x86_64':
1106            mn = mp.m_hdr.mh_next
1107        else:
1108            mn = mp.M_hdr_common.M_hdr.mh_next
1109        while (mn):
1110            nxt = nxt + 1
1111            if kern.arch == 'x86_64':
1112                mn = mn.m_hdr.mh_next
1113            else:
1114                mn = mn.M_hdr_common.M_hdr.mh_next
1115            print("mp 0x{:x} mn 0x{:x}".format(mp, mn))
1116
1117        if kern.arch == 'x86_64':
1118            mp = mp.m_hdr.mh_nextpkt
1119        else:
1120            mp = mp.M_hdr_common.M_hdr.mh_nextpkt
1121
1122        if (((pkt + nxt) % 50) == 0):
1123            print(" ..." + str(pkt_nxt))
1124
1125    print("Total: " + str(pkt + nxt) + " (via m_next: " + str(nxt) + ")")
1126# EndMacro: mbuf_countchain
1127
1128# Macro: mbuf_topleak
1129@lldb_command('mbuf_topleak')
1130def MbufTopLeak(cmd_args=None):
1131    """ Print the top suspected mbuf leakers
1132    """
1133    if kern.arch != 'x86_64':
1134        print("mcache is disabled, use zleak")
1135        return
1136    topcnt = 0
1137    if (int(len(cmd_args)) > 0 and int(cmd_args[0]) < 5):
1138        maxcnt = cmd_args[0]
1139    else:
1140        maxcnt = 5
1141    while (topcnt < maxcnt):
1142        print(GetMbufTraceLeak(kern.globals.mleak_top_trace[topcnt]))
1143        topcnt += 1
1144
1145# EndMacro: mbuf_topleak
1146
1147def GetMbufTraceLeak(trace):
1148    out_string = ""
1149    if (trace != 0 and trace.allocs != 0):
1150        out_string += hex(trace) + ":" + str(trace.allocs) + " outstanding allocs\n"
1151        out_string += "Backtrace saved " + str(trace.depth) + " deep\n"
1152        if (trace.depth != 0):
1153            cnt = 0
1154            while (cnt < trace.depth):
1155                out_string += str(cnt + 1) + ": "
1156                out_string += GetPc(trace.addr[cnt])
1157                out_string += "\n"
1158                cnt += 1
1159    return out_string
1160
1161@lldb_command('mbuf_largefailures')
1162def MbufLargeFailures(cmd_args=None):
1163    """ Print the largest allocation failures
1164    """
1165    if kern.arch != 'x86_64':
1166        print("mcache is disabled, this macro is not available. use zleak to detect leaks")
1167        return
1168    topcnt = 0
1169    if (int(len(cmd_args)) > 0 and int(cmd_args[0]) < 5):
1170        maxcnt = cmd_args[0]
1171    else:
1172        maxcnt = 5
1173    while (topcnt < maxcnt):
1174        trace = kern.globals.mtracelarge_table[topcnt]
1175        if (trace.size == 0):
1176            topcnt += 1
1177            continue
1178        print(str(trace.size))
1179        if (trace.depth != 0):
1180            cnt = 0
1181            while (cnt < trace.depth):
1182                print(str(cnt + 1) + ": " + GetPc(trace.addr[cnt]))
1183                cnt += 1
1184        topcnt += 1
1185
1186
1187# Macro: mbuf_traceleak
1188@lldb_command('mbuf_traceleak')
1189def MbufTraceLeak(cmd_args=None):
1190    """ Print the leak information for a given leak address
1191        Given an mbuf leak trace (mtrace) structure address, print out the
1192        stored information with that trace
1193        syntax: (lldb) mbuf_traceleak <addr>
1194    """
1195    if cmd_args is None or len(cmd_args) == 0:
1196        raise ArgumentError("Missing argument 0 in user function.")
1197
1198    if kern.arch != 'x86_64':
1199        print("mcache is disabled, use kasan whatis")
1200        return
1201
1202    trace = kern.GetValueFromAddress(cmd_args[0], 'mtrace *')
1203    print(GetMbufTraceLeak(trace))
1204# EndMacro: mbuf_traceleak
1205
1206
1207# Macro: mcache_walkobj
1208@lldb_command('mcache_walkobj')
1209def McacheWalkObject(cmd_args=None):
1210    """ Given a mcache object address, walk its obj_next pointer
1211    """
1212    if cmd_args is None or len(cmd_args) == 0:
1213        raise ArgumentError("Missing argument 0 in user function.")
1214
1215    if kern.arch != 'x86_64':
1216        print("mcache is disabled, use kasan whatis")
1217        return
1218
1219    out_string = ""
1220    p = kern.GetValueFromAddress(cmd_args[0], 'mcache_obj_t *')
1221    cnt = 1
1222    total = 0
1223    while (p):
1224        mcache_object_format = "{0:>4d}: 0x{1:>16x}"
1225        out_string += mcache_object_format.format(cnt, p) + "\n"
1226        p = p.obj_next
1227        cnt += 1
1228    print(out_string)
1229# EndMacro: mcache_walkobj
1230
1231# Macro: mcache_stat
1232@lldb_command('mcache_stat')
1233def McacheStat(cmd_args=None):
1234    """ Print all mcaches in the system.
1235    """
1236    if kern.arch != 'x86_64':
1237        print("mcache is disabled, use kasan whatis")
1238        return
1239
1240    head = kern.globals.mcache_head
1241    out_string = ""
1242    mc = cast(head.lh_first, 'mcache *')
1243    if (kern.ptrsize == 8):
1244        mcache_stat_format_string = "{0:<24s} {1:>8s} {2:>20s} {3:>5s} {4:>5s} {5:>20s} {6:>30s} {7:>18s}"
1245    else:
1246        mcache_stat_format_string = "{0:<24s} {1:>8s} {2:>12s} {3:>5s} {4:>5s} {5:>12s} {6:>30s} {7:>18s}"
1247
1248    if (kern.ptrsize == 8):
1249        mcache_stat_data_format_string = "{0:<24s} {1:>12s} {2:>20s} {3:>5s} {4:>5s} {5:>22s} {6:>12d} {7:>8d} {8:>8d} {9:>18d}"
1250    else:
1251        mcache_stat_data_format_string = "{0:<24s} {1:>12s} {2:>12s} {3:>5s} {4:>5s} {5:>14s} {6:>12d} {7:>8d} {8:>8d} {9:>18d}"
1252
1253    out_string += mcache_stat_format_string.format("cache name", "cache state", "cache addr", "buf size", "buf align", "backing zone", "wait     nowait     failed", "bufs incache")
1254    out_string += "\n"
1255
1256    ncpu = int(kern.globals.ncpu)
1257    while mc != 0:
1258        bktsize = mc.mc_cpu[0].cc_bktsize
1259        cache_state = ""
1260        if (mc.mc_flags & MCF_NOCPUCACHE):
1261            cache_state = "disabled"
1262        else:
1263            if (bktsize == 0):
1264                cache_state = " offline"
1265            else:
1266                cache_state = " online"
1267        if (mc.mc_slab_zone != 0):
1268            backing_zone = mc.mc_slab_zone
1269        else:
1270            if (kern.ptrsize == 8):
1271                backing_zone = "            custom"
1272            else:
1273                backing_zone = "    custom"
1274
1275        total = 0
1276        total += mc.mc_full.bl_total * bktsize
1277        n = 0
1278        while(n < ncpu):
1279            ccp = mc.mc_cpu[n]
1280            if (ccp.cc_objs > 0):
1281                total += ccp.cc_objs
1282            if (ccp.cc_pobjs > 0):
1283                total += ccp.cc_pobjs
1284            n += 1
1285
1286        out_string += mcache_stat_data_format_string.format(mc.mc_name, cache_state, hex(mc), str(int(mc.mc_bufsize)), str(int(mc.mc_align)), hex(mc.mc_slab_zone), int(mc.mc_wretry_cnt), int(mc.mc_nwretry_cnt), int(mc.mc_nwfail_cnt), total)
1287        out_string += "\n"
1288        mc = cast(mc.mc_list.le_next, 'mcache *')
1289    print(out_string)
1290# EndMacro: mcache_stat
1291
1292# Macro: mcache_showcache
1293@lldb_command('mcache_showcache')
1294def McacheShowCache(cmd_args=None):
1295    """Display the number of objects in cache.
1296    """
1297    if kern.arch != 'x86_64':
1298        print("mcache is disabled, use kasan whatis")
1299        return
1300    out_string = ""
1301    cp = kern.GetValueFromAddress(cmd_args[0], 'mcache_t *')
1302    bktsize = cp.mc_cpu[0].cc_bktsize
1303    cnt = 0
1304    total = 0
1305    mcache_cache_format = "{0:<4d} {1:>8d} {2:>8d} {3:>8d}"
1306    out_string += "Showing cache " + str(cp.mc_name) + " :\n\n"
1307    out_string += " CPU  cc_objs cc_pobjs    total\n"
1308    out_string += "----  ------- -------- --------\n"
1309    ncpu = int(kern.globals.ncpu)
1310    while (cnt < ncpu):
1311        ccp = cp.mc_cpu[cnt]
1312        objs = ccp.cc_objs
1313        if (objs <= 0):
1314            objs = 0
1315        pobjs = ccp.cc_pobjs
1316        if (pobjs <= 0):
1317            pobjs = 0
1318        tot_cpu = objs + pobjs
1319        total += tot_cpu
1320        out_string += mcache_cache_format.format(cnt, objs, pobjs, tot_cpu)
1321        out_string += "\n"
1322        cnt += 1
1323
1324    out_string += "                       ========\n"
1325    out_string += "                           " + str(total) + "\n\n"
1326    total += cp.mc_full.bl_total * bktsize
1327
1328    out_string += "Total # of full buckets (" + str(int(bktsize)) + " objs/bkt):\t" + str(int(cp.mc_full.bl_total)) + "\n"
1329    out_string += "Total # of objects cached:\t\t" + str(total) + "\n"
1330    print(out_string)
1331# EndMacro: mcache_showcache
1332
1333# Macro: mbuf_wdlog
1334@lldb_command('mbuf_wdlog')
1335def McacheShowWatchdogLog(cmd_args=None):
1336    """Display the watchdog log
1337    """
1338    lldb_run_command('settings set max-string-summary-length 4096')
1339    print('%s' % lldb_run_command('p/s mbwdog_logging').replace("\\n","\n"))
1340# EndMacro: mbuf_wdlog
1341