Thursday, September 25, 2014

DMG path from mount point on OS X

Getting the DMG file path for a mount point using Disk arbitration framework.

input CFURLRef volumePathRef;

//Create DADiskRef from a given volume
DASessionRef session = DASessionCreate(NULL);
DADiskRef diskRef = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, volumePathRef);
DADiskRef wholeDiskRef = DADiskCopyWholeDisk(diskRef);
CFDictionaryRef wholeDiskDesc = DADiskCopyDescription(wholeDiskRef);

//Get device path
CFStringRef val = NULL;
if (CFDictionaryGetValueIfPresent(wholeDiskDesc, kDADiskDescriptionDevicePathKey, (const void**)&val))
{ 
    //Convert CFString to C-string
    CFStringRef devPathRef = (CFStringRef*)val;
    char devPath[1024];//Assuming 1024 max len, may need more, allocate dynamically.
    memset(devPath, 0, sizeof(devPath));
    CFRange range = CFRangeMake(0, CFStringGetLength(devPathRef));
    CFStringGetBytes(devPathRef, range, kCFStringEncodingUTF8, '?', FALSE, (UInt8 *)&devPath[0], 1024, NULL);


    io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, devPath);
    if (entry)
    {
        //Get Protocol Characteristics
        CFTypeRef ref = IORegistryEntryCreateCFProperty(entry, CFSTR(kIOPropertyProtocolCharacteristicsKey), kCFAllocatorDefault, 0);
        if (ref)
        {
            //Get DMG path
            CFDictionaryRef props = (CFDictionaryRef)ref;
            const void *val = NULL;
            if (CFDictionaryGetValueIfPresent(props, CFSTR("Virtual Interface Location Path"), &val))
            {
                const char*path = (const char*)CFDataGetBytePtr((CFDataRef)val);
                std::cout << "DMG Path " << path <<"\n";
            }
        }
    }
}

Note: CFRelease/free/delete allocated/copied resources.

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