Saturday, August 30, 2014

Workaround Max thread limit on OS X

OS X limits max threads, max threads per process and max processes based on total physical memory (and  whether serverperfmode is set in boot-args)
see xnu osfmk/kern/startup.c : scale_setup()

For testing, sometimes we may need more threads than these limits allow.
The limits are exposed via sysctl but only as a read only value.

We can however alter the limits using a kernel extension.

This involves writing to kernel private symbols using a method given by Evan Lojewski at
ref: http://lists.apple.com/archives/darwin-kernel/2010/Jul/msg00017.html

For per process max thread limit we have to set the global variable task_threadmax and task_max
see xnu osfmk/kern/startup.c : scale_setup()

  • First build kextsymboltool (xnu/SETUP/kextsymboltool)
If you have built xnu then the binary should be available, otherwise build using
clang -I/usr/include/ -F/System/Library/Frameworks/ -framework System -lstdc++ kextsymboltool.c -o kextsymboltool

  • Then create helper kext binary having private symbols
NAME=MissingSymbols
nm -gj /mach_kernel > allsymbols

echo "_task_threadmax" > ${NAME}.exports
echo "_task_max" >> ${NAME}.exports

kextsymboltool -arch x86_64 -import allsymbols -export ${NAME}.exports -output ${NAME}

  • Create a plugin Kext
Place MissingSymbols binary and a new Info.plist with following contents in a directory named MissingSymbols.kext
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>CFBundleDevelopmentRegion</key>
        <string>English</string>
        <key>CFBundleExecutable</key>
        <string>MissingSymbols</string>
        <key>CFBundleGetInfoString</key>
        <string>MissingSymbols</string>
        <key>CFBundleIdentifier</key>
        <string>com.abc.xyz.MissingSymbols</string>
        <key>CFBundleInfoDictionaryVersion</key>
        <string>6.0</string>
        <key>CFBundleName</key>
        <string>Symbols for Raise Thread limit</string>
        <key>CFBundlePackageType</key>
        <string>KEXT</string>
        <key>CFBundleShortVersionString</key>
        <string>10.0.0</string>
        <key>CFBundleSignature</key>
        <string>????</string>
        <key>CFBundleVersion</key>
        <string>10.0.0</string>
        <key>OSBundleCompatibleVersion</key>
        <string>8.0.0</string>
        <key>OSBundleRequired</key>
        <string>Root</string>
        <key>OSKernelResource</key>
        <true/>
        <key>OSBundleAllowUserLoad</key>
        <true/>
</dict>
</plist>



  • Create main Kext (RaiseMaxThreads.kext) that overrides the limits

#include <mach/mach_types.h>

kern_return_t RaiseMaxThreads_start(kmod_info_t * ki, void *d);
kern_return_t RaiseMaxThreads_stop(kmod_info_t *ki, void *d);

extern int task_threadmax;
extern int task_max;
static int task_threadmax_old = 0;
static int task_max_old = 0;

kern_return_t RaiseMaxThreads_start(kmod_info_t * ki, void *d)
{
    task_threadmax_old = task_threadmax;
    task_max_old = task_max;
    task_max = task_threadmax = 20000;
    return KERN_SUCCESS;
}

kern_return_t RaiseMaxThreads_stop(kmod_info_t *ki, void *d)
{
    if (task_threadmax_old != 0) {
        task_threadmax = task_threadmax_old;
        task_max = task_max_old;
    }
    return KERN_SUCCESS;
}


  • Copy the MissingSymbols.kext to
RaiseMaxThreads.kext/Contents/PlugIns
  • Add com.abc.xyz.MissingSymbols to the OSBundleLibraries in Info.plist for RaiseMaxThreads.kext
  • Load the kext and test using
sysctl kern.num_taskthreads

1 comment:

  1. I'm wondering whether it is still working with OSx Mojave in 2019? I mean, I'm afraid I could mess up my whole system with a not 100% correct kernel extension, can I?

    ReplyDelete