Files
darling-lol/tools/build-riot-kqueue-runtime.py
T
lucannez64andClaude Opus 5.5 4370f375cd Cut Darling overhead in League fights and add passive match capture
Game process (ARAM Hexgate: 84 -> 127 FPS average, slowest stretches ~110):
- Local pthread mutexes, condition variables and Mach semaphores are on by
  default in the game process only, with /tmp/rclol-no-local-* opt-outs.
  The main thread's darlingserver waits fell from 1.36 to 0.10 ms per frame.
- Local semaphores: the count and the slot generation change in one
  compare-exchange, a destroy leaves a negative marker, the table is reset
  after fork, and names the table did not create go to the server.
- Metal: drop identical re-binds (keyed on the Indium object, depth-stencil
  retained), pass blit resources by reference, and stop resetting vertex
  descriptors twice.
- Core isolation of the main thread is opt-in (/tmp/rclol-core-isolation);
  it lowered FPS in its only match.
- Optimized libc/libc++/Foundation overlays, AVX2 string primitives, real CPU
  topology and their installers and offline checks.

Tools and docs:
- tools/riot-match-capture: passive /proc sampler, user-space IP profiler and
  reports, plus a comparison against the game's netstats.
- tools/riot-lsem-model and tools/riot-metal-bind-model: host models that
  extract the shipped code and check it.
- tools/install-riot-metal-pair.sh installs Metal and Indium together.
- Status, setup, testing and troubleshooting docs updated with the measured
  matches and the switch table; manifest file counts match the patches.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 16:48:34 +02:00

127 lines
9.7 KiB
Python

#!/usr/bin/env python3
"""Build the queue-lifetime repair and its descriptor-leak regression probe."""
from pathlib import Path
import os
import shlex
import subprocess
project = Path(__file__).resolve().parent.parent
build = project / 'build/darling-src/build'
kernel=build/'src/external/xnu/darling/src/libsystem_kernel'
objects=shlex.split((kernel/'CMakeFiles/system_kernel.dir/objects1.rsp').read_text())
import re
# The saved CMake flags name the clang resource directory of the compiler that configured the tree (22); the system
# compiler is 23 now and <stdbool.h> disappears. Follow the installed clang.
resource_dir=subprocess.check_output(['clang','-print-resource-dir'],text=True).strip()
def configured_flags(directory):
result={}
for line in (directory/'flags.make').read_text().splitlines():
if line.startswith('C_') and ' = ' in line:
key,value=line.split(' = ',1)
result[key]=[re.sub(r'/usr/lib/clang/\d+/include',resource_dir+'/include',v) for v in shlex.split(value)]
return result
kqueue_flags=configured_flags(build/'src/external/libkqueue/CMakeFiles/kqueue.dir')
emulation_flags=configured_flags(kernel/'emulation/CMakeFiles/emulation.dir')
syscall_flags=configured_flags(kernel/'libsyscall/CMakeFiles/libsyscall.dir')
# Build every configured kqueue object with optimization; the configured
# upstream target explicitly uses -O0 even when the rest of the runtime is
# optimized. Preserve its definitions, debug checks, and source selection.
kqueue_flags={**kqueue_flags,'C_FLAGS':[*kqueue_flags['C_FLAGS'],'-O2','-fno-strict-aliasing']}
kqueue_sources=[value.split('/kqueue.dir/',1)[1][:-2] for value in objects if '/kqueue.dir/' in value and value.endswith('.c.o')]
assert kqueue_sources, 'Configured kqueue objects missing'
# The configured emulation target is a plain development build: its flags.make
# carries no -O flag, so every object recompiled with `emulation_flags` is -O0.
# These files are on the per-call path of the guest (BSD trap dispatch, read/
# write, network, kqueue, psynch/ulock, clocks, madvise), so rebuild them
# optimized. -fno-strict-aliasing matches the other optimized rebuilds in this
# repository; the sources are old BSD/xnu C.
repair_flags={**emulation_flags,'C_FLAGS':[*emulation_flags['C_FLAGS'],'-O2','-fno-strict-aliasing']}
syscall_repair_flags={**syscall_flags,'C_FLAGS':[*syscall_flags['C_FLAGS'],'-O2','-fno-strict-aliasing']}
repairs=[(source,kqueue_flags,project/'build/darling-src/src/external/libkqueue') for source in kqueue_sources]
repairs += [('src/xnu_syscall/bsd/bsd_syscall_table.c',repair_flags,project/'build/darling-src/src/external/xnu/darling/src/libsystem_kernel/emulation')]
repairs += [('src/common/base.c',repair_flags,project/'build/darling-src/src/external/xnu/darling/src/libsystem_kernel/emulation')]
# Avoid a redundant absolute-time read when the commpage timestamp is unpublished.
repairs += [('wrappers/__commpage_gettimeofday.c',syscall_repair_flags,project/'build/darling-src/src/external/xnu/libsyscall')]
repairs += [('src/xnu_syscall/bsd/impl/'+name,repair_flags,
project/'build/darling-src/src/external/xnu/darling/src/libsystem_kernel/emulation')
for name in ('bsdthread/pthread_canceled.c','kqueue/kevent_qos.c','bsdthread/workq_kernreturn.c','psynch/psynch_cvwait.c','psynch/psynch_mutexwait.c','psynch/psynch_mutexdrop.c','psynch/ulock_wait.c',
# the remaining psynch/ulock traps stay RPCs but are still per-wait
'psynch/psynch_cvbroad.c','psynch/psynch_cvclrprepost.c','psynch/psynch_cvsignal.c',
'psynch/psynch_rw_rdlock.c','psynch/psynch_rw_unlock.c','psynch/psynch_rw_wrlock.c','psynch/ulock_wake.c',
# select busy-loop trace (opt-in) and the network flag conversions
'select/select.c','network/recvfrom.c','network/recvmsg.c','network/sendto.c','network/sendmsg.c','unistd/read.c','unistd/close.c','process/fork.c',
# the allocator's reclamation hint
'mman/madvise.c')]
# RPC guards use the full native signal mask. Rebuild both users of the
# configured forced-include header, without changing unrelated kernel objects.
emulation_root=project/'build/darling-src/src/external/xnu/darling/src/libsystem_kernel/emulation'
rpc_flags={**emulation_flags,'C_FLAGS':[*emulation_flags['C_FLAGS'],'-O2','-fno-omit-frame-pointer']}
# bsdthread_register.c keeps emulation_flags (-O0): its pthread/wqueue entry
# wrappers jump into _thread_start/_start_wqthread through inline assembly that
# sets %rsp from the thread argument, i.e. it depends on the stack layout.
# Signal entry with stack-layout-dependent frame walking stays unoptimized on
# purpose (emulation_flags): it runs on the fault/suspend signal stacks.
repairs += [('src/xnu_syscall/bsd/impl/bsdthread/bsdthread_register.c',emulation_flags,emulation_root)]
repairs += [('src/linux_premigration/signal/sigexc.c',emulation_flags,emulation_root)]
repairs += [('src/xnu_syscall/mach/impl/mach_traps.c',rpc_flags,emulation_root)]
# User-space clock (per-CPU TSC calibration), its gettimeofday entry and the cached kern.boottime.
repairs += [('src/linux_premigration/ext/clock_gettime.c',rpc_flags,emulation_root)]
repairs += [('src/xnu_syscall/bsd/impl/time/gettimeofday.c',rpc_flags,emulation_root)]
repairs += [('src/other/mach/lkm.c',rpc_flags,emulation_root)]
# sysctl_kern.c takes the emulated OS identity from per-source definitions (emulation/CMakeLists.txt)
identity=['-DEMULATED_SYSNAME="Darwin"','-DEMULATED_RELEASE="23.6.0"',
'-DEMULATED_VERSION="Darwin Kernel Version 23.6.0: Mon Jul 29 21:14:30 PDT 2024; root:xnu-10063.141.2~1/RELEASE_X86_64"',
'-DEMULATED_OSVERSION="23H124"','-DEMULATED_OSPRODUCTVERSION="14.7"']
repairs += [('src/xnu_syscall/bsd/helper/misc/sysctl_kern.c',{**emulation_flags,'C_FLAGS':[*emulation_flags['C_FLAGS'],*identity]},emulation_root)]
# The hw.*/machdep.cpu.* sysctls are not on the per-call path: recompile them
# with the configured flags only because these sources changed (the same way
# bsdthread_register.c keeps -O0). sysctl_hw.c now reports the host's real CPU
# topology; sysctl_machdep.c shares its probe for core_count/thread_count.
repairs += [('src/xnu_syscall/bsd/helper/misc/sysctl_hw.c',emulation_flags,emulation_root)]
repairs += [('src/xnu_syscall/bsd/helper/misc/sysctl_machdep.c',emulation_flags,emulation_root)]
repairs += [('src/linux_premigration/resources/dserver-rpc-defs.c',rpc_flags,emulation_root)]
forced_rpc_flags={**rpc_flags,'C_FLAGS':[*rpc_flags['C_FLAGS'],'-include',str(emulation_root/'include/linux_premigration/resources/dserver-rpc-defs.h')]}
repairs += [('src/rpc.c',forced_rpc_flags,build/'src/external/darlingserver')]
# Process-local Mach semaphores (mach_traps.c) wrap semaphore_create/semaphore_destroy: the MIG stubs get private names.
mig_flags=configured_flags(kernel/'libsyscall/CMakeFiles/libsyscall_64.dir')
mig_obj=project/'build/riot-kernel-task-x86_64-User.o'
subprocess.run(['clang',*mig_flags['C_DEFINES'],*mig_flags['C_INCLUDES'],*mig_flags['C_FLAGS'],
'-Dsemaphore_create=__darling_rpc_semaphore_create','-Dsemaphore_destroy=__darling_rpc_semaphore_destroy',
'-c',str(kernel/'libsyscall/mach/task-x86_64-User.c'),'-o',str(mig_obj)],check=True,cwd=kernel/'libsyscall')
matches=[i for i,value in enumerate(objects) if value.endswith('mach/task-x86_64-User.c.o')]
assert len(matches)==1,matches
objects[matches[0]]=str(mig_obj)
# mach_vm_region_recurse on the own task is answered in the process (mach_traps.c); the MIG stub gets a private name.
vm_obj=project/'build/riot-kernel-mach_vm-x86_64-User.o'
subprocess.run(['clang',*mig_flags['C_DEFINES'],*mig_flags['C_INCLUDES'],*mig_flags['C_FLAGS'],
'-Dmach_vm_region_recurse=__darling_rpc_mach_vm_region_recurse',
'-c',str(kernel/'libsyscall/mach/mach_vm-x86_64-User.c'),'-o',str(vm_obj)],check=True,cwd=kernel/'libsyscall')
matches=[i for i,value in enumerate(objects) if value.endswith('mach/mach_vm-x86_64-User.c.o')]
assert len(matches)==1,matches
objects[matches[0]]=str(vm_obj)
for source,settings,root in repairs:
obj=project/('build/riot-kernel-'+source.replace('/','-')+'.o')
subprocess.run(['clang',*settings['C_DEFINES'],*settings['C_INCLUDES'],*settings['C_FLAGS'],
'-c',str(root/source),'-o',str(obj)],check=True)
matches=[i for i,value in enumerate(objects) if value.endswith('/'+source+'.o')]
assert len(matches)==1,(source,matches)
objects[matches[0]]=str(obj)
rsp=project/'build/riot-kernel-objects.rsp'
rsp.write_text(shlex.join(objects))
link=shlex.split((kernel/'CMakeFiles/system_kernel.dir/link.txt').read_text())
link[link.index('@CMakeFiles/system_kernel.dir/objects1.rsp')]='@'+str(rsp)
link[link.index('-o')+1]=str(project/'build/riot-libsystem_kernel.dylib')
subprocess.run(link,cwd=kernel,check=True)
flags = {}
for line in (build / 'src/frameworks/CoreVideo/CMakeFiles/CoreVideo.dir/flags.make').read_text().splitlines():
if line.startswith('C_') and ' = ' in line:
name, value = line.split(' = ', 1)
flags[name] = shlex.split(value)
for probe in ('riot-task-info-check','riot-kqueue-lifetime-check','riot-kqueue-stack-check','riot-workqueue-reuse-check','riot-dispatch-fd-check','riot-kqueue-dispatch2-check','riot-syscall-fastpath-check','riot-select-backoff-check','riot-vnode-close-check','riot-rpc-signal-mask-check','riot-fileport-lifetime-check','riot-kqueue-timer-check','riot-cancel-rpc-check'):
obj = project / f'build/{probe}.o'
subprocess.run(['clang', *flags['C_DEFINES'], *flags['C_INCLUDES'], *flags['C_FLAGS'],
'-O2', '-c', str(project / f'tools/{probe}.c'), '-o', str(obj)], check=True)
subprocess.run(['clang', '-target', 'x86_64-apple-macosx11.0', '-fuse-ld=lld', '-nostdlib',
'-isysroot', '@DARLING_SDK@', str(obj), '-lSystem', '-o',
str(project / f'build/{probe}')], check=True)