docs: def Regions(self,
                skip_executable_regions=False,
                skip_shared_regions=False,
                skip_readonly_regions=False):
      """Iterates over the readable regions for this process.
  
      We use mach_vm_region_recurse here to get a fine grained view of
      the process' memory space. The algorithm is that for some regions,
      the function returns is_submap=True which means that there are
      actually subregions that we need to examine by increasing the
      depth and calling the function again. For example, there are two
      regions, addresses 1000-2000 and 2000-3000 where 1000-2000 has two
      subregions, 1100-1200 and 1300-1400. In that case we would call:
  
      mvrr(address=0, depth=0)       -> (1000-2000, is_submap=True)
      mvrr(address=0, depth=1)       -> (1100-1200, is_submap=False)
      mvrr(address=1200, depth=1)    -> (1300-1400, is_submap=False)
      mvrr(address=1400, depth=1)    -> (2000-3000, is_submap=False)
  
      At this point, we know we went out of the original submap which
      ends at 2000. We need to recheck the region at 2000, it could be
      submap = True at depth 0 so we call
  
      mvrr(address=1400, depth=0)    -> (2000-3000, is_submap=False)
  
      Args:
        skip_executable_regions: Skips executable sections.
        skip_shared_regions: Skips shared sections. Includes mapped files.
        skip_readonly_regions: Skips readonly sections.
  
      Yields:
        Pairs (address, length) for each identified region.
      """
  
      address = ctypes.c_ulong(0)
      mapsize = ctypes.c_ulong(0)
      count = ctypes.c_uint32(submap_info_size)
      sub_info = vm_region_submap_short_info_data_64()
      depth = 0
      depth_end_addresses = {}
  
      while True:
        c_depth = ctypes.c_uint32(depth)
  
        r = libc.mach_vm_region_recurse(self.task, ctypes.pointer(address),
                                        ctypes.pointer(mapsize),
                                        ctypes.pointer(c_depth),
                                        ctypes.pointer(sub_info),
                                        ctypes.pointer(count))
  
        # If we get told "invalid address", we have crossed into kernel land...
        if r == 1:
          break
  
        if r != 0:
          raise process_error.ProcessError("Error in mach_vm_region, ret=%s" % r)
  
        if depth > 0 and address.value >= depth_end_addresses[depth]:
          del depth_end_addresses[depth]
          depth -= 1
          continue
  
        p = sub_info.protection
        if skip_executable_regions and p & VM_PROT_EXECUTE:
          address.value += mapsize.value
          continue
  
        if skip_shared_regions and sub_info.share_mode in [
            SM_COW, SM_SHARED, SM_TRUESHARED
        ]:
          address.value += mapsize.value
          continue
  
        if not p & VM_PROT_READ:
          address.value += mapsize.value
          continue
  
        writable = p & VM_PROT_WRITE
        if skip_readonly_regions and not writable:
          address.value += mapsize.value
          continue
  
        if sub_info.is_submap:
          depth += 1
          depth_end_addresses[depth] = address.value + mapsize.value
        else:
          yield address.value, mapsize.value
          address.value += mapsize.value