Determining memory usage in process on OSX

My strenuous journey started with a seemingly simple task: I wanted to obtain a rough and tumble estimate of the amount of memory instantiating a rather opaque data structure from a third party library would consume.

Naively, I started writing a loop that created a new instance sleeping and then … I poked around Single Unix for a system call that could help and ended up coming up with `getrusage`. OSX man pages stated it fills in this:

struct rusage {
  struct timeval ru_utime; /* user time used */
  struct timeval ru_stime; /* system time used */
  long ru_maxrss;          /* integral max resident set size */
  long ru_ixrss;           /* integral shared text memory size */
  long ru_idrss;           /* integral unshared data size */
  long ru_isrss;           /* integral unshared stack size */
  long ru_minflt;          /* page reclaims */
  long ru_majflt;          /* page faults */
  long ru_nswap;           /* swaps */
  long ru_inblock;         /* block input operations */
  long ru_oublock;         /* block output operations */
  long ru_msgsnd;          /* messages sent */
  long ru_msgrcv;          /* messages received */
  long ru_nsignals;        /* signals received */
  long ru_nvcsw;           /* voluntary context switches */
  long ru_nivcsw;          /* involuntary context switches */
};

Unfortunately, all the fields apart from user and system time remain zeroed. `grepping` through the xnu (Darwin kernel, apparently the only available information about what does and doesn’t work under OSX) sources, I ended up finding this comment in the declaration of `struct rusage` which finally convinced me that I’m not too stupid to make a simple call:

struct    rusage {
    struct timeval ru_utime;    /* user time used (PL) */
    struct timeval ru_stime;    /* system time used (PL) */
#if defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE)
    long    ru_opaque[14];        /* implementation defined */
#else    /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */
    /*
     * Informational aliases for source compatibility with programs
     * that need more information than that provided by standards,
     * and which do not mind being OS-dependent.
     */
    long    ru_maxrss;        /* max resident set size (PL) */
#define    ru_first    ru_ixrss    /* internal: ruadd() range start */
    long    ru_ixrss;        /* integral shared memory size (NU) */
    (...)
};

At least the good folks over at SUN are kind enough to mention the fact all the fields are dummies in their man pages.

Then I found Michael Knight’s (not that one) blog, which used the underlying mach function `task_info`. Unfortunately, Apple doesn’t document the mach API at all and the sole reference they supply points directly to nowhere.

Well, if `ps` can determine memory usage, surely it should be able to tell me how. Finding the source of OSX `ps` was another story. Hint: it’s not located in the `basic_cmds`, `misc_cmds`, `shell_cmds`, or `system_cmds` package. It’s in the `adv_cmds` (advice?, advanced?, adventure?).

`ps` ended up using a bunch of equally undocumented (non-mach) kernel functions. At this point I remembered that `macfuse` contains a `procfs` for OSX. To me, using `proc` seems to be the obvious way to get memory usage under Linux, so I dug through that and saw macfuse uses `task_info` as well.

I finally found documentation for the mach API within the xnu sources under the `osfmk/man` directory or online here and was able to write a simplified version of Michael’s original.

Voila:

#include <mach/task.h>

int getmem (unsigned int *rss, unsigned int *vs)
{
    task_t task = MACH_PORT_NULL;
    struct task_basic_info t_info;
    mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;

    if (KERN_SUCCESS != task_info(mach_task_self(),
       TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count))
    {
        return -1;
    }
    *rss = t_info.resident_size;
    *vs  = t_info.virtual_size;
    return 0;
}

In case anyone knows of an even remotely portable way obtain similar information, please let me know.

7 Kommentare zu „Determining memory usage in process on OSX“

  1. simoncpu sagt:

    Hi,

    I don’t have my MacBook right now, but doesn’t Mac OS X support kvm_getprocs(3) too? I’ve discovered it while reading FreeBSD’s ps utility.

    Regards,

    [ simon.cpu ]

  2. Willem sagt:

    Amazing. This helped me out.

  3. Rohit sagt:

    Excellent – this was very useful. The task_info works great. I had to add an additional header:

    #include

    Thanks for documenting this.

  4. Rohit sagt:

    mach slash mach_init.h

  5. dfighter sagt:

    Thanks a bunch! This is just what I was looking for.
    Altough to make it work, you also have to
    #include

    Cheer!

  6. dfighter sagt:

    Oups the blog engine ate the include.
    So anyhow, you also need to include mach.h for this to work
    Again cheers!

  7. Thomas Stüfe sagt:

    Still works, thanks!