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.