Google Analytics

Thursday, November 10, 2005

Resident Set Size in Mac OS X

A process lives in its own segment of virtual memory. Not all of this virtual memory is in RAM though. The 'bits' (typically called 'pages') that are in RAM make up what is known as the resident set size (RSS) of a process. As a process runs, the active parts need to be brought into RAM, becoming part of the RSS. Unused pages may be removed (discarded completely or written back to disk if they've changed from the on-disk copy). It's up to the operating system when to do this. Usually when the system is low on physical RAM the OS will start removing pages.

You can view the RSS of a process on most Unices (Linux, Mac OS X included) by bringing up a terminal and going ps -o pid,ucomm,rss (it will probably be given in kilobytes). Or, try the popular top program, which may report it under the heading of 'RES' or 'RSIZE'.

For my thesis I needed to write a program that did some stuff and reported its effective memory usage at the same time. I also had to get it to run on several operating systems. OS X posed quite a challenge as it has no /proc pseudo file system. I couldn't simply open a file and pull out the right number.

After a heck of a lot of digging I eventually found this and this and this, not to mention the source code for the ps utility itself (yay OpenDarwin). This got me pretty close, and took me forever. However, it wasn't until I found a document in Google's cache (that 404'd when I tried it) by Chaz McGarvey that I got over the line.

Here's the code:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/task.h>
#include <mach/mach_init.h>

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

task_info(task, TASK_BASIC_INFO, (task_info_t)&t_info, &t_info_count);
*rss = t_info.resident_size;
*vs = t_info.virtual_size;
}

void memcheck()
{
unsigned int rss, vs, psize;
task_t task = MACH_PORT_NULL;

if (task_for_pid(current_task(), getpid(), &task) != KERN_SUCCESS)
abort();
getres(task, &rss, &vs);
psize = getpagesize();
fprintf(stderr, "RSS: %u KiB, VS: %u KiB.\n", rss, vs);
}

And that should report the RSS and total VM size of the current process that invokes this code (in bytes). It's probably not perfect (it should have more error checking) but hopefully it will get me by.

No comments: