macosperformance-testingisodmgdvd

How to simulate slow DVD drive?


Does anyone know any feasible ways to simulate a slow DVD drive, e.g. over a mounted DMG/ISO image?

The goal is to program against a slow drive, thus a simulation requirement. Any ideas would be greatly appreciated!

Update: again, the goal would be to simulate a slow I/O process. Tools like Network Conditioner or Charles do not provide a solution unfortunately. trickle is out of date and no more actively developed :(


Solution

  • With hdiutil, you could mount a disk image over a specially crafted HTTP server, but you do not control the OS cache and I/O slowness would not be fine-grained. I would suggest two non-network solutions.

    Inserting slowness in I/O system calls

    You could do slow-down I/O system calls, for example through DYLD_INSERT_LIBRARIES. This approach is quite easy and this is what I would try first.

    You simply create a library with read(2) and pread(2) implementations like:

    /* slow.c */
    #define SLEEP_TIMESPEC {0, 25000000} // 25ms
    ssize_t
    read(int fildes, void *buf, size_t nbyte) {
        struct timespec s = SLEEP_TIMESPEC;
        (void) nanosleep(&s, NULL);
        return (ssize_t) syscall(SYS_read, fildes, buf, nbyte);
    }
    
    ssize_t
    pread(int d, void *buf, size_t nbyte, off_t offset) {
        struct timespec s = SLEEP_TIMESPEC;
        (void) nanosleep(&s, NULL);
        return (ssize_t) syscall(SYS_pread, d, buf, nbyte, offset);
    }
    

    You might also need to implement readv(2). You simply need to compile this C code as a shared library and set DYLD_INSERT_LIBRARIES to load this library before running your program. You will probably also need to define DYLD_FORCE_FLAT_NAMESPACE. See dyld(1).

    clang -arch i386 -arch x86_64 -shared -Wall slow.c -o slow.dylib
    

    (The library is compiled universally, as the AIR app I had on disk was actually i386, not x86_64).

    To test the library, simply do:

    env DYLD_INSERT_LIBRARIES=slow.dylib DYLD_FORCE_FLAT_NAMESPACE=1 cat slow.c
    

    You might want to try with values higher than 25ms for cat, e.g. 1 second which can be inlined as {1, 0}. Likewise, you should start your application from the command line:

    env DYLD_INSERT_LIBRARIES=slow.dylib DYLD_FORCE_FLAT_NAMESPACE=1 path/to/YourApp.app/Contents/MacOS/YourApp
    

    This will slow down every read calls (even through higher-level APIs). Some read operations will not be affected (e.g. mmap(2)), however, and you might want to slow down I/Os on some files but not on others. This later case can be handled by trapping open(2) but requires more work.

    25ms per read accesses is enough to make any AIR app noticeably slower. Of course, you should adjust this value to your needs.

    Working with a slower file system

    Alternatively, you could implement a Fuse plug-in. This is especially easy if you start from LoopbackFS (C or ObjC).

    Indeed, you can very easily call nanosleep(2) in the readFileAtPath:userData:buffer:size:offset:error: method or loopback_read function.