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