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.