4

Certain operations in my app are using more memory than I think they should, and I would like to log the current memory usage to help identify which they are.

Is there a system call that will return the amount of memory currently in use?

David Sykes
  • 48,469
  • 17
  • 71
  • 80

4 Answers4

8

The following C function returns the CPU time and resident memory of process pid. To get the resources of other processes, you need root permission. You may also try getrusage(), but I never get it work properly for memory usage. Getting CPU time with getrusage() always works to me.

The function is adapted from the source codes of the ps and top commands. It is part of my program that monitors the memory of other processes.

#ifdef __APPLE__

#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/vmmeter.h>
#include <mach/mach_init.h>
#include <mach/mach_host.h>
#include <mach/mach_port.h>
#include <mach/mach_traps.h>
#include <mach/task_info.h>
#include <mach/thread_info.h>
#include <mach/thread_act.h>
#include <mach/vm_region.h>
#include <mach/vm_map.h>
#include <mach/task.h>
#include <mach/shared_memory_server.h>

typedef struct vmtotal vmtotal_t;

typedef struct { /* dynamic process information */
    size_t rss, vsize;
    double utime, stime;
} RunProcDyn;

/* On Mac OS X, the only way to get enough information is to become root. Pretty frustrating!*/
int run_get_dynamic_proc_info(pid_t pid, RunProcDyn *rpd)
{
    task_t task;
    kern_return_t error;
    mach_msg_type_number_t count;
    thread_array_t thread_table;
    thread_basic_info_t thi;
    thread_basic_info_data_t thi_data;
    unsigned table_size;
    struct task_basic_info ti;

    error = task_for_pid(mach_task_self(), pid, &task);
    if (error != KERN_SUCCESS) {
        /* fprintf(stderr, "++ Probably you have to set suid or become root.\n"); */
        rpd->rss = rpd->vsize = 0;
        rpd->utime = rpd->stime = 0;
        return 0;
    }
    count = TASK_BASIC_INFO_COUNT;
    error = task_info(task, TASK_BASIC_INFO, (task_info_t)&ti, &count);
    assert(error == KERN_SUCCESS);
    { /* adapted from ps/tasks.c */
        vm_region_basic_info_data_64_t b_info;
        vm_address_t address = GLOBAL_SHARED_TEXT_SEGMENT;
        vm_size_t size;
        mach_port_t object_name;
        count = VM_REGION_BASIC_INFO_COUNT_64;
        error = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO,
                             (vm_region_info_t)&b_info, &count, &object_name);
        if (error == KERN_SUCCESS) {
            if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) &&
                ti.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE))
            {
                ti.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE);
            }
        }
        rpd->rss = ti.resident_size;
        rpd->vsize = ti.virtual_size;
    }
    { /* calculate CPU times, adapted from top/libtop.c */
        unsigned i;
        rpd->utime = ti.user_time.seconds + ti.user_time.microseconds * 1e-6;
        rpd->stime = ti.system_time.seconds + ti.system_time.microseconds * 1e-6;
        error = task_threads(task, &thread_table, &table_size);
        assert(error == KERN_SUCCESS);
        thi = &thi_data;
        for (i = 0; i != table_size; ++i) {
            count = THREAD_BASIC_INFO_COUNT;
            error = thread_info(thread_table[i], THREAD_BASIC_INFO, (thread_info_t)thi, &count);
            assert(error == KERN_SUCCESS);
            if ((thi->flags & TH_FLAGS_IDLE) == 0) {
                rpd->utime += thi->user_time.seconds + thi->user_time.microseconds * 1e-6;
                rpd->stime += thi->system_time.seconds + thi->system_time.microseconds * 1e-6;
            }
            if (task != mach_task_self()) {
                error = mach_port_deallocate(mach_task_self(), thread_table[i]);
                assert(error == KERN_SUCCESS);
            }
        }
        error = vm_deallocate(mach_task_self(), (vm_offset_t)thread_table, table_size * sizeof(thread_array_t));
        assert(error == KERN_SUCCESS);
    }
    mach_port_deallocate(mach_task_self(), task);
    return 0;
}

#endif /* __APPLE__ */
user172818
  • 4,518
  • 1
  • 18
  • 20
  • Very useful piece of code. One comment though: in the code as posted, the statement mach_port_deallocate(mach_task_self(), thread_table[i]) is protected by the condition (task != mach_task_self()), and mach_port_deallocate(mach_task_self(), task) (at the end) is run in all cases. I believe this should be switched (it's the latter statement which should be executed conditionally), after running into trouble with calling mach_port_deallocate(mach_task_self(), task) with task == mach_task_self(). This is supported by documentation which states that mach_task_self() returns a cached value. – tcovo Mar 01 '11 at 21:06
  • This is very useful. Unfortunately though has been marked as deprecated in the current SDK – the_mandrill May 10 '12 at 15:54
  • The macros GLOBAL_SHARED_TEXT_SEGMENT, SHARED_TEXT_REGION_SIZE and SHARED_DATA_REGION_SIZE are defined in though, and not in . – martemiev Jan 29 '15 at 23:32
  • Do you if there are man pages for `thread_info`, etc., on OS X? I see none on OS X Yosemite. – Drux Feb 19 '15 at 09:18
  • Is there a way to subtract the shared memory from the resident size? – rstackhouse Mar 13 '15 at 15:10
  • Does `task_info ` also contains the process cpu usage as it appears in `/usr/bin/top` command under the title `%CPU` ? thanks ! –  Aug 23 '18 at 07:56
2

launch your application with Instruments. put it through the paces, and evaluate the results...

kent
  • 6,286
  • 4
  • 27
  • 32
  • Instruments is certainly a remarkably useful program, thanks, but I don't always have access to the application – David Sykes Oct 12 '09 at 09:43
2

Following @user172818 advice, I tried getrusage and it worked for me:

#include <sys/time.h>
#include <sys/resource.h>

long getMemoryUsage() 
{
  struct rusage usage;
  if(0 == getrusage(RUSAGE_SELF, &usage))
    return usage.ru_maxrss; // bytes
  else
    return 0;
}

I am using Mac OS X 10.9.4, with compiler Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn).

ChronoTrigger
  • 8,459
  • 1
  • 36
  • 57
  • 1
    Tried this and it did not seem to account for memory that was ```free```d and thus returned inaccurate and bloated figures – mwag Nov 29 '19 at 20:36
  • That's probably because the `malloc`ed memory is not returned to the SO by `free`, the process reuses it for future `malloc`s. – ChronoTrigger Dec 01 '19 at 22:38
  • Whatever the reason may be, while the number were similar at the very start of running the program, by the time it was finished, the figures given by ru_maxrss were several orders of magnitude larger than those given by tools such as the Activity Monitor or valgrind+massif – mwag Dec 01 '19 at 23:32
1

You can give a try to mallocDebug function : http://developer.apple.com/mac/library/DOCUMENTATION/Performance/Conceptual/ManagingMemory/Articles/FindingPatterns.html.

Patrice Bernassola
  • 14,136
  • 6
  • 46
  • 59