xref: /xnu-10002.61.3/tools/lldbmacros/kmemory/zone.py (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
1from __future__ import absolute_import, division, print_function
2
3from builtins import range
4from builtins import object
5
6import six
7import struct
8
9from core import (
10    caching,
11    gettype,
12    lldbwrap,
13    xnu_format,
14)
15from .kmem   import KMem, MemoryRange
16from .btlog  import BTLog, BTLibrary
17from .whatis import *
18
19# FIXME: should not import this from xnu / utils
20from xnu import (
21    GetSourceInformationForAddress,
22    print_hex_data,
23)
24
25class ZoneBitsMemoryObject(MemoryObject):
26    """ Memory Object for pointers in the Zone Bitmaps range """
27
28    MO_KIND = "zone bitmap"
29
30    @property
31    def object_range(self):
32        return self.kmem.bits_range
33
34    def describe(self, verbose=False):
35        #
36        # Printing something more useful would require crawling
37        # all zone chunks with non inline bitmaps until we find
38        # the one.
39        #
40        # This is very expensive and really unlikely to ever
41        # be needed for debugging.
42        #
43        # Moreover, bitmap pointers do not leak outside
44        # of the bowels of zalloc, dangling pointers to
45        # this region is very unexpected.
46        #
47        print("Zone Bitmap Info")
48        print(" N/A")
49        print()
50
51
52class ZonePageMetadata(MemoryObject):
53    """ Memory Object for Zone Page Metadata """
54
55    MO_KIND = "zone metadata"
56
57    def __init__(self, kmem, address):
58        super(ZonePageMetadata, self).__init__(kmem, address)
59
60        if not kmem.meta_range.contains(address):
61            raise IndexError("{:#x} is not inside the meta range {}".format(
62                address, kmem.meta_range))
63
64        #
65        # Resolve the ZPM we fall into
66        #
67        size = kmem.zpm_type.GetByteSize()
68        idx  = (address - kmem.meta_range.start) // size
69        sbv  = kmem.target.xCreateValueFromAddress(None,
70            kmem.meta_range.start + idx * size, kmem.zpm_type)
71        chunk_len = sbv.xGetIntegerByName('zm_chunk_len')
72
73        self.mo_sbv = sbv
74        self.kmem   = kmem
75
76        #
77        # Compute the canonical ZPM
78        #
79        # 0xe = ZM_SECONDARY_PAGE
80        # 0xf = ZM_SECONDARY_PCPU_PAGE
81        #
82        # TODO use a nice package to index enums by name,
83        #      can't use GetEnumName() because it uses kern.*
84        #
85        if chunk_len in (0xe, 0xf):
86            pg_idx    = sbv.xGetIntegerByName('zm_page_index')
87            idx      -= pg_idx
88            sbv       = sbv.xGetSiblingValueAtIndex(-pg_idx)
89            chunk_len = sbv.xGetIntegerByName('zm_chunk_len')
90
91        self.sbv        = sbv
92        self._idx       = idx
93        self._chunk_len = chunk_len
94
95    @classmethod
96    def _create_with_zone_address(cls, kmem, address):
97        zone_range = kmem.zone_range
98        if not zone_range.contains(address):
99            raise IndexError("{:#x} is not inside the zone map {}".format(
100                address, zone_range))
101
102        index     = (address - zone_range.start) >> kmem.page_shift
103        meta_addr = kmem.meta_range.start + index * kmem.zpm_type.GetByteSize()
104
105        return ZonePageMetadata(kmem, meta_addr)
106
107    @classmethod
108    def _create_with_pva(cls, kmem, pva):
109        address = ((pva | 0xffffffff00000000) << kmem.page_shift) & 0xffffffffffffffff
110        return ZonePageMetadata._create_with_zone_address(kmem, address)
111
112    @property
113    def object_range(self):
114        addr = self.sbv.GetLoadAddress()
115        clen = self._chunk_len
116        if clen == 1 and self.zone.percpu:
117            clen  = self.kmem.ncpus
118        size = self._chunk_len * self.kmem.zpm_type.GetByteSize()
119
120        return MemoryRange(addr, addr + size)
121
122    @property
123    def zone(self):
124        sbv = self.sbv
125        return Zone(sbv.xGetIntegerByName('zm_index'))
126
127    @property
128    def pgz_slot(self):
129        addr = self.page_addr
130        kmem = self.kmem
131        if kmem.pgz_range.contains(addr):
132            return (addr - kmem.pgz_range.start) >> (kmem.page_shift + 1)
133        return None
134
135    def _pgz_alloc_frames(self, index):
136        kmem   = self.kmem
137        target = kmem.target
138        bt     = kmem.pgz_bt.xGetSiblingValueAtIndex(index)
139        return (
140            kmem.stext + pc
141            for pc in target.xIterAsInt32(
142                bt.xGetLoadAddressByName('pgz_bt'),
143                bt.xGetIntegerByName('pgz_depth')
144            )
145        )
146
147    @property
148    def pgz_alloc_bt_frames(self):
149        return self._pgz_alloc_frames(2 * self.pgz_slot)
150
151    @property
152    def pgz_free_bt_frames(self):
153        return self._pgz_alloc_frames(2 * self.pgz_slot + 1)
154
155    def describe(self, verbose=False):
156        kmem = self.kmem
157        sbv  = self.sbv
158        zone = self.zone
159
160        chunk_len = self._chunk_len
161        if zone.percpu:
162            chunk_len = kmem.ncpus
163
164        zone.describe()
165
166        print("Zone Metadata Info")
167        print(" chunk length         : {}".format(chunk_len))
168        print(" metadata             : {:#x}".format(sbv.GetLoadAddress()))
169        print(" page                 : {:#x}".format(self.page_addr))
170
171        if sbv.xGetIntegerByName('zm_inline_bitmap'):
172            if verbose:
173                bitmap = [
174                    "{:#010x}".format(sbv.xGetSiblingValueAtIndex(i).xGetIntegerByName('zm_bitmap'))
175                    for i in range(self._chunk_len)
176                ]
177                print(" bitmap               : inline [ {} ]".format(" ".join(bitmap)))
178            else:
179                print(" bitmap               : inline")
180        else:
181            bref   = sbv.xGetIntegerByName('zm_bitmap')
182            blen   = 1 << ((bref >> 29) & 0x7)
183            bsize  = blen << 3
184            baddr  = kmem.bits_range.start + 8 * (bref & 0x0fffffff)
185            bitmap = (
186                "{:#018x}".format(word)
187                for word in kmem.target.xIterAsUInt64(baddr, blen)
188            )
189
190            if bref == 0:
191                print(" bitmap               : None")
192            elif not verbose:
193                print(" bitmap               : {:#x} ({} bytes)".format(baddr, bsize))
194            elif blen <= 2:
195                print(" bitmap               : {:#x} ({} bytes) [ {} ]".format(
196                    baddr, bsize, ' '.join(bitmap)))
197            else:
198                print(" bitmap               : {:#x} ({} bytes) [".format(baddr, bsize))
199                for i in range(blen // 4):
200                    print("  {}  {}  {}  {}".format(
201                        next(bitmap), next(bitmap),
202                        next(bitmap), next(bitmap)))
203                print(" ]")
204
205        print()
206
207        mo_sbv = self.mo_sbv
208        if sbv != mo_sbv:
209            pg_idx = self.mo_sbv.xGetIntegerByName('zm_page_index')
210
211            print("Secondary Metadata Info")
212            print(" index                : {}/{}".format(pg_idx + 1, chunk_len))
213            print(" metadata             : {:#x}".format(mo_sbv.GetLoadAddress()))
214            print(" page                 : {:#x}".format(
215                self.page_addr + (pg_idx << kmem.page_shift)))
216            print()
217
218        if verbose:
219            print("-" * 80)
220            print()
221            print(str(self.mo_sbv))
222            print()
223
224
225    @property
226    def next_pva(self):
227        """ the next zone_pva_t queued after this Zone Page Metadata """
228
229        return self.sbv.xGetIntegerByPath('.zm_page_next.packed_address')
230
231    @property
232    def page_addr(self):
233        """ The page address corresponding to this Zone Page Metadata """
234
235        kmem = self.kmem
236        return kmem.zone_range.start + (self._idx << kmem.page_shift)
237
238    def iter_all(self, zone):
239        """ All element addresses covered by this chunk """
240
241        base  = self.page_addr
242        esize = zone.elem_outer_size
243        offs  = zone.elem_inner_offs
244        count = zone.chunk_elems
245        run   = self.sbv.xGetIntegerByName('zm_chunk_len')
246
247        return range(base + offs, base + (run << self.kmem.page_shift), esize)
248
249    def is_allocated(self, zone, addr):
250        """ Whether an address has the allocated bit set """
251
252        if not self._chunk_len:
253            return False
254
255        sbv   = self.sbv
256        base  = self.page_addr + zone.elem_inner_offs
257        esize = zone.elem_inner_size
258        idx   = (addr - base) // esize
259
260        if sbv.xGetIntegerByName('zm_inline_bitmap'):
261            w, b = divmod(idx, 32)
262            mask = sbv.xGetSiblingValueAtIndex(w).xGetIntegerByName('zm_bitmap')
263            return (mask & (1 << b)) == 0
264        else:
265            w, b  = divmod(idx, 64)
266            bref  = sbv.xGetIntegerByName('zm_bitmap')
267            kmem  = self.kmem
268            baddr = kmem.bits_range.start + 8 * (bref & 0x0fffffff) + 8 * w
269            return not (kmem.target.xReadUInt64(baddr) & (1 << b))
270
271    def iter_allocated(self, zone):
272        """ All allocated addresses in this this chunk """
273
274        kmem  = self.kmem
275        sbv   = self.sbv
276        base  = self.page_addr
277
278        # cache memory, can make enumeration twice as fast for smaller objects
279        sbv.target.xReadBytes(base, self._chunk_len << kmem.page_shift)
280
281        esize = zone.elem_outer_size
282        base += zone.elem_inner_offs
283
284        if sbv.xGetIntegerByName('zm_inline_bitmap'):
285            for i in range(zone.chunk_elems):
286                w, b = divmod(i, 32)
287                if b == 0:
288                    mask = sbv.xGetSiblingValueAtIndex(w).xGetIntegerByName('zm_bitmap')
289                if not mask & (1 << b):
290                    yield base + i * esize
291        else:
292            bref  = sbv.xGetIntegerByName('zm_bitmap')
293            baddr = kmem.bits_range.start + 8 * (bref & 0x0fffffff)
294            data  = kmem.target.xIterAsUInt64(baddr, 1 << ((bref >> 29) & 0x7))
295
296            for i in range(zone.chunk_elems):
297                b = i & 63
298                if b == 0:
299                    word = next(data)
300                if not word & (1 << b):
301                    yield base + i * esize
302
303
304class ZoneHeapMemoryObject(MemoryObject):
305    """ Memory Object for zone allocated objects """
306
307    MO_KIND = "zone heap"
308
309    def __init__(self, kmem, address):
310        super(ZoneHeapMemoryObject, self).__init__(kmem, address)
311
312        if not kmem.zone_range.contains(address):
313            raise IndexError("{:#x} is not inside the zone range {}".format(
314                address, kmem.zone_range))
315
316        meta  = ZonePageMetadata._create_with_zone_address(kmem, address)
317        zone  = meta.zone
318        esize = zone.elem_outer_size
319
320        if kmem.pgz_range.contains(address):
321            real_addr = meta.sbv.xGetIntegerByName('zm_pgz_orig_addr')
322            page_mask = kmem.page_mask
323            elem_addr = (real_addr & page_mask) + (address & ~page_mask)
324            elem_idx  = ((elem_addr & page_mask) - zone.elem_inner_offs) // esize
325            self.real_addr = real_addr
326            self.real_meta = ZonePageMetadata._create_with_zone_address(kmem, real_addr)
327            self.pgz       = True
328        else:
329            base      = meta.page_addr + zone.elem_inner_offs
330            elem_idx  = (address - base) // esize if address >= base else -1
331            elem_addr = base + elem_idx * esize   if address >= base else None
332            self.real_addr = elem_addr
333            self.real_meta = meta
334            self.pgz       = False
335
336        self.kmem      = kmem
337        self.meta      = meta
338        self.zone      = zone
339        self.elem_idx  = elem_idx
340        self.elem_addr = elem_addr
341
342    @property
343    def object_range(self):
344        if self.elem_idx >= 0:
345            elem_addr = self.elem_addr
346            elem_size = self.zone.elem_outer_size
347            return MemoryRange(elem_addr, elem_addr + elem_size)
348
349        base = self.meta.page_addr
350        size = self.zone.elem_inner_offs
351        return MemoryRange(base, base + size)
352
353    @property
354    def status(self):
355        zone      = self.zone
356        real_addr = self.real_addr
357
358        if self.elem_idx < 0:
359            return "invalid"
360
361        elif not self.real_meta.is_allocated(zone, real_addr):
362            return "free"
363
364        elif real_addr in zone.cached():
365            return "free (cached)"
366
367        elif real_addr in zone.recirc():
368            return "free (recirc)"
369
370        else:
371            return "allocated"
372
373    def hexdump(self):
374        print("Hexdump:")
375
376        target = self.kmem.target
377        zone   = self.zone
378        eaddr  = self.elem_addr
379        eend   = eaddr + zone.elem_inner_size
380        delta  = self.real_addr - eaddr
381
382        rz     = zone.elem_redzone
383        start  = (eaddr & -16) - min(rz, 16) - 16
384        end    = (eend + 16 + 15) & -16
385        marks  = { self.address: '>' }
386
387        if rz > 16:
388            print(" " + "=" * 88)
389            print(" {}".format("." * 18))
390
391            try:
392                data = target.xReadBytes(start + delta, eaddr - start)
393                print_hex_data(data, start, "", marks)
394            except:
395                print(" *** unable to read redzone memory ***")
396        else:
397            try:
398                data = target.xReadBytes(start + delta, eaddr - rz - start)
399                print_hex_data(data, start, "", marks)
400            except:
401                pass
402
403            print(" " + "=" * 88)
404
405            if rz:
406                try:
407                    data = target.xReadBytes(eaddr - rz + delta, rz)
408                    print_hex_data(data, eaddr - rz, "", marks)
409                except:
410                    print(" *** unable to read redzone memory ***")
411
412        if rz:
413            print(" {}".format("-" * 88))
414
415        try:
416            data = target.xReadBytes(eaddr + delta, eend - eaddr)
417            print_hex_data(data, eaddr, "", marks)
418        except:
419            print(" *** unable to read element memory ***")
420
421        print(" " + "=" * 88)
422
423        try:
424            data = target.xReadBytes(eend + delta, end - eend)
425            print_hex_data(data, eend, "", marks)
426        except:
427            pass
428
429        print()
430
431    def describe(self, verbose=False):
432        meta   = self.meta
433        zone   = self.zone
434        status = self.status
435        btlog  = zone.btlog
436
437        meta.describe()
438
439        print("Zone Heap Object Info")
440        print(" element index        : {}".format(self.elem_idx))
441        print(" chunk offset         : {}".format(self.address - meta.page_addr))
442        print(" status               : {}".format(status))
443        if self.pgz:
444            print(" pgz orig address     : {:#x}".format(self.real_addr))
445            print()
446
447            print("PGZ Allocation backtrace:")
448            for pc in meta.pgz_alloc_bt_frames:
449                print(" " + GetSourceInformationForAddress(pc))
450
451            if status == 'free':
452                print()
453
454                print("PGZ Free backtrace:")
455                for pc in meta.pgz_free_bt_frames:
456                    print(" " + GetSourceInformationForAddress(pc))
457        elif btlog and (btlog.is_log() or status == 'allocated'):
458            record = next(btlog.iter_records(
459                wantElement=self.elem_addr, reverse=True), None)
460            if record:
461                btlib = BTLibrary.get_shared()
462                print(" last zlog backtrace",
463                    *btlib.get_stack(record.ref).symbolicated_frames(prefix="  "), sep="\n")
464
465        print()
466
467        if self.elem_idx >= 0 and verbose:
468            self.hexdump()
469
470
471@whatis_provider
472class ZoneWhatisProvider(WhatisProvider):
473    """
474    Whatis Provider for the zone ranges
475    - metadata (bits and ZPM)
476    - PGZ
477    - regular heap objects
478    """
479
480    def __init__(self, kmem):
481        super(ZoneWhatisProvider, self).__init__(kmem)
482
483    def claims(self, address):
484        kmem = self.kmem
485
486        return any(
487            r.contains(address)
488            for r in (kmem.meta_range, kmem.bits_range, kmem.zone_range)
489        )
490
491    def lookup(self, address):
492        kmem = self.kmem
493
494        if kmem.meta_range.contains(address):
495            return ZonePageMetadata(self.kmem, address)
496
497        if kmem.bits_range.contains(address):
498            return ZoneBitsMemoryObject(self.kmem, address)
499
500        return ZoneHeapMemoryObject(self.kmem, address)
501
502
503class ZPercpuValue(object):
504    """
505    Provides an enumerator for a zpercpu value
506    """
507
508    def __init__(self, sbvalue):
509        """
510        @param sbvalue (SBValue)
511            The value to enumerate
512        """
513        self.sbv = sbvalue
514
515    def __iter__(self):
516        sbv  = self.sbv
517        kmem = KMem.get_shared()
518        addr = sbv.GetValueAsAddress() | 0xc0c0000000000000
519        name = sbv.GetName()
520        ty   = sbv.GetType().GetPointeeType()
521
522        return (
523            sbv.xCreateValueFromAddress(name, addr + (cpu << kmem.page_shift), ty)
524            for cpu in kmem.zcpus
525        )
526
527
528class Zone(object):
529    """
530    the Zone class wraps XNU Zones and provides fast enumeration
531    of allocated, cached, ... elements.
532    """
533
534    def __init__(self, index_name_or_addr):
535        """
536        @param index_name_or_addr (int or str):
537            - int: a zone index within [0, num_zones)
538            - int: a zone address within [zone_array, zone_array + num_zones)
539            - str: a zone name
540
541        @param kmem (KMem or None)
542            The kmem this command applies to,
543            or None for the current one
544        """
545
546        kmem = KMem.get_shared()
547        zarr = kmem.zone_array
548
549        if isinstance(index_name_or_addr, six.string_types):
550            mangled_name = index_name_or_addr.replace(' ', '.')
551            zid = self._find_zone_id_by_mangled_name(mangled_name)
552        elif index_name_or_addr <= kmem.num_zones:
553            zid = index_name_or_addr
554        else:
555            zid = index_name_or_addr - zarr.GetLoadAddress()
556            zid = zid // zarr.GetType().GetArrayElementType().GetByteSize()
557
558        self.kmem = kmem
559        self.zid  = zid
560        self.sbv  = zarr.chkGetChildAtIndex(zid)
561
562    @staticmethod
563    @caching.cache_dynamically
564    def get_zone_name(zid, target=None):
565        """
566        Returns a zone name by index.
567
568        @param zid (int
569            A zone ID
570
571        @returns (str or None)
572            Returns a string holding the zone name
573            if the zone exists, or None
574        """
575
576        kmem = KMem.get_shared()
577        if zid >= kmem.num_zones:
578            return None
579
580        zone = kmem.zone_array.chkGetChildAtIndex(zid)
581        zsec = kmem.zsec_array.chkGetChildAtIndex(zid)
582
583        if zone.xGetIntegerByName('z_self') == 0:
584            return None
585
586        heap_id = zsec.xGetIntegerByName('z_kheap_id')
587
588        return KMem._HEAP_NAMES[heap_id] + zone.xGetCStringByName('z_name')
589
590    @staticmethod
591    @caching.cache_dynamically
592    def _find_zone_id_by_mangled_name(name, target=None):
593        """
594        Lookup a zone ID by name
595
596        @param name (str)
597            The name of the zone to lookup
598
599        @returns (int)
600            The zone ID for this name
601        """
602
603        kmem = KMem.get_shared()
604        for zid in range(kmem.num_zones):
605            k = Zone.get_zone_name(zid)
606            if k is not None and name == k.replace(' ', '.'):
607                return zid
608
609        raise KeyError("No zone called '{}' found".format(name))
610
611    @property
612    def initialized(self):
613        """ The zone name """
614
615        return self.sbv.xGetIntegerByName('z_self') != 0
616
617    @property
618    def address(self):
619        """ The zone address """
620
621        return self.sbv.GetLoadAddress()
622
623    @property
624    def name(self):
625        """ The zone name """
626
627        return self.get_zone_name(self.zid)
628
629    @property
630    def mangled_name(self):
631        """ The zone mangled name """
632
633        return self.name.replace(' ', '.')
634
635    @caching.dyn_cached_property
636    def elem_redzone(self, target=None):
637        """ The inner size of elements """
638
639        if self.kmem.kasan_classic:
640            return self.sbv.xGetIntegerByName('z_kasan_redzone')
641        return 0
642
643    @caching.dyn_cached_property
644    def elem_inner_size(self, target=None):
645        """ The inner size of elements """
646
647        return self.sbv.xGetIntegerByName('z_elem_size')
648
649    @caching.dyn_cached_property
650    def elem_outer_size(self, target=None):
651        """ The size of elements """
652
653        if not self.kmem.kasan_classic:
654            return self.elem_inner_size
655        return self.elem_inner_size + self.elem_redzone
656
657    @caching.dyn_cached_property
658    def elem_inner_offs(self, target=None):
659        """ The chunk initial offset """
660
661        return self.sbv.xGetIntegerByName('z_elem_offs')
662
663    @caching.dyn_cached_property
664    def chunk_pages(self, target=None):
665        """ The number of pages per chunk """
666
667        return self.sbv.xGetIntegerByName('z_chunk_pages')
668
669    @caching.dyn_cached_property
670    def chunk_elems(self, target=None):
671        """ The number of elements per chunk """
672
673        return self.sbv.xGetIntegerByName('z_chunk_elems')
674
675    @property
676    def percpu(self):
677        """ Whether this is a per-cpu zone """
678
679        return self.sbv.xGetIntegerByName('z_percpu')
680
681    @property
682    def btlog(self):
683        """ Returns the zone's BTLog or None """
684
685        try:
686            btlog = zone.sbv.xGetPointeeByName('z_btlog')
687            return BTLog(btlog)
688        except:
689            return None
690
691    def describe(self):
692        kmem = self.kmem
693        zone = self.sbv
694        zsec = kmem.zsec_array.chkGetChildAtIndex(self.zid)
695
696        submap_arr  = kmem.target.chkFindFirstGlobalVariable('zone_submaps_names')
697        submap_idx  = zsec.xGetIntegerByName('z_submap_idx')
698        submap_name = submap_arr.xGetCStringAtIndex(submap_idx)
699        submap_end  = zsec.xGetIntegerByName('z_submap_from_end')
700
701        try:
702            btlog = zone.xGetIntegerByName('z_btlog')
703        except:
704            # likely a release kernel
705            btlog = None
706
707        fmt = (
708            "Zone Info\n"
709            " name                 : {0.name} ({&z:#x})\n"
710            " submap               : {1} (from {2})\n"
711            " element size         : {0.elem_inner_size}\n"
712            " element offs         : {0.elem_inner_offs}\n"
713        )
714        if kmem.kasan_classic:
715            fmt += " element redzone      : {0.elem_redzone}\n"
716        fmt += " chunk elems / pages  : {$z.z_chunk_elems} / {$z.z_chunk_pages}\n"
717        if btlog:
718            fmt += " btlog                : {$z.z_btlog:#x}\n"
719
720        print(xnu_format(fmt, self, submap_name,
721            "right" if submap_end else "left", z = zone));
722
723    def iter_page_queue(self, name):
724        kmem = self.kmem
725        zone = self.sbv
726
727        pva = zone.xGetIntegerByPath('.{}.packed_address'.format(name))
728
729        while pva:
730            meta = ZonePageMetadata._create_with_pva(kmem, pva)
731            pva  = meta.next_pva
732            yield meta
733
734    def _depotElements(self, depot, into):
735        last   = depot.xGetPointeeByName('zd_tail').GetValueAsAddress()
736        mag    = depot.xGetPointeeByName('zd_head')
737
738        kmem   = self.kmem
739        n      = kmem.mag_size
740        target = kmem.target
741
742        while mag and mag.GetLoadAddress() != last:
743            into.update(kmem.iter_addresses(target.xIterAsULong(
744                mag.xGetLoadAddressByName('zm_elems'),
745                n
746            )))
747            mag = mag.xGetPointeeByName('zm_next')
748
749        return into
750
751    def cached(self, into = None):
752        """ all addresses in per-cpu caches or per-cpu depots """
753
754        pcpu = self.sbv.GetChildMemberWithName('z_pcpu_cache')
755        into = into or set()
756
757        if pcpu.GetValueAsAddress():
758            target = pcpu.target
759            kmem   = self.kmem
760
761            for cache in ZPercpuValue(pcpu):
762                into.update(kmem.iter_addresses(target.xIterAsULong(
763                    cache.xGetIntegerByName('zc_alloc_elems'),
764                    cache.xGetIntegerByName('zc_alloc_cur')
765                )))
766
767                into.update(kmem.iter_addresses(target.xIterAsULong(
768                    cache.xGetIntegerByName('zc_free_elems'),
769                    cache.xGetIntegerByName('zc_free_cur')
770                )))
771
772                self._depotElements(
773                    cache.chkGetChildMemberWithName('zc_depot'),
774                    into = into
775                )
776
777        return into
778
779    def recirc(self, into = None):
780        """ all addresses in the recirculation layer """
781
782        return self._depotElements(
783            self.sbv.chkGetChildMemberWithName('z_recirc'),
784            into = into or set()
785        )
786
787    def iter_all(self, ty = None):
788        """
789        Returns a generator for all addresses/values that can be made
790
791        @param ty (SBType or None)
792            An optional type to use to form SBValues
793
794        @returns
795            - (generator<int>) if ty is None
796            - (generator<SBValue>) if ty is set
797        """
798
799        addresses = (
800            addr
801            for name in (
802                'z_pageq_full',
803                'z_pageq_partial',
804                'z_pageq_empty',
805            )
806            for meta in self.iter_page_queue(name)
807            for addr in meta.iter_all(self)
808        )
809
810        if ty is None:
811            return addresses
812
813        fn = self.kmem.target.xCreateValueFromAddress
814        return (fn('e', addr, ty) for addr in addresses)
815
816    def iter_free(self, ty = None):
817        """
818        Returns a generator for all free addresses/values
819
820        @param ty (SBType or None)
821            An optional type to use to form SBValues
822
823        @returns
824            - (generator<int>) if ty is None
825            - (generator<SBValue>) if ty is set
826        """
827
828        cached = set()
829        self.cached(into = cached)
830        self.recirc(into = cached)
831
832        addresses = (
833            addr
834            for name in (
835                'z_pageq_full',
836                'z_pageq_partial',
837            )
838            for meta in self.iter_page_queue(name)
839            for addr in meta.iter_all(self)
840            if  addr in cached or not meta.is_allocated(self, addr)
841        )
842
843        if ty is None:
844            return addresses
845
846        fn = self.kmem.target.xCreateValueFromAddress
847        return (fn('e', addr, ty) for addr in addresses)
848
849    def iter_allocated(self, ty = None):
850        """
851        Returns a generator for all allocated addresses/values
852
853        @param ty (SBType or None)
854            An optional type to use to form SBValues
855
856        @returns
857            - (generator<int>) if ty is None
858            - (generator<SBValue>) if ty is set
859        """
860
861        cached = set()
862        self.cached(into = cached)
863        self.recirc(into = cached)
864
865        addresses = (
866            addr
867            for name in (
868                'z_pageq_full',
869                'z_pageq_partial',
870            )
871            for meta in self.iter_page_queue(name)
872            for addr in meta.iter_allocated(self)
873            if  addr not in cached
874        )
875
876        if ty is None:
877            return addresses
878
879        fn = self.kmem.target.xCreateValueFromAddress
880        return (fn('e', addr, ty) for addr in addresses)
881
882    def __iter__(self):
883        return self.iter_allocated()
884
885
886__all__ = [
887    ZPercpuValue.__name__,
888    Zone.__name__,
889]
890