mirror of
https://github.com/lucannez64/darling-lol.git
synced 2026-10-27 19:54:38 +00:00
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]>
127 lines
9.7 KiB
Python
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)
|