c++macossystem-callsdtracedtruss

Dtruss not showing mmap/sbrk calls?


I've recently decided to learn more about systems programming, and felt it would be helpful to see what my code is actually doing under the hood.

To do this, I wrote a short LinkedList class in C++ and decided to trace it using dtruss (read: dtrace).

My expectation was that any instructions that extend the heap (e.g. using the new keyword, or instantiating LinkedList objects) would invoke the mmap or sbrk/break system calls. This was not the case!

In fact, running dtruss with the -s switch, I don't see any system calls being invoked from inside my LinkedList::Add function! Testing, I'm certain elements are being added.

Can anyone explain why I don't see references to mmap/sbrk in my dtruss output?

Bonus points if someone could explain the purpose of mprotect and madvise.

I've included my LinkedList class, main.cpp, and dtruss output below.

Thank you!

dtruss output

SYSCALL(args)        = return
Created new LinkedList
Created new LinkedList
Destroyed a LinkedList
open("/dev/dtracehelper\0", 0x2, 0xFFFFFFFFE3236D70)         = 3 0
ioctl(0x3, 0x80086804, 0x7FFEE3236CD0)       = 0 0
close(0x3)       = 0 0
access("/AppleInternal/XBS/.isChrooted\0", 0x0, 0x0)         = -1 Err#2
thread_selfid(0x0, 0x0, 0x0)         = 198178 0
bsdthread_register(0x7FFF5BAB5C50, 0x7FFF5BAB5C40, 0x2000)       = 1073742047 0
issetugid(0x0, 0x0, 0x0)         = 0 0
mprotect(0x10C9D0000, 0x1000, 0x0)       = 0 0
mprotect(0x10C9D5000, 0x1000, 0x0)       = 0 0
mprotect(0x10C9D6000, 0x1000, 0x0)       = 0 0
mprotect(0x10C9DB000, 0x1000, 0x0)       = 0 0
mprotect(0x10C9CE000, 0x88, 0x1)         = 0 0
mprotect(0x10C9DC000, 0x1000, 0x1)       = 0 0
mprotect(0x10C9CE000, 0x88, 0x3)         = 0 0
mprotect(0x10C9CE000, 0x88, 0x1)         = 0 0
getpid(0x0, 0x0, 0x0)        = 1698 0
stat64("/AppleInternal/XBS/.isChrooted\0", 0x7FFEE32362E8, 0x0)      = -1 Err#2
stat64("/AppleInternal\0", 0x7FFEE3236380, 0x0)      = -1 Err#2
csops(0x6A2, 0x7, 0x7FFEE3235E20)        = -1 Err#22
sysctl([CTL_KERN, 14, 1, 1698, 0, 0] (4), 0x7FFEE3235F68, 0x7FFEE3235F60, 0x0, 0x0)      = 0 0
csops(0x6A2, 0x7, 0x7FFEE3235710)        = -1 Err#22
getrlimit(0x1008, 0x7FFEE32374F0, 0x0)       = 0 0
fstat64(0x1, 0x7FFEE3237508, 0x0)        = 0 0
ioctl(0x1, 0x4004667A, 0x7FFEE3237554)       = 0 0
write_nocancel(0x1, "Created new LinkedList\n\0", 0x17)      = 23 0
write_nocancel(0x1, "Created new LinkedList\n\0", 0x17)      = 23 0
write_nocancel(0x1, "Destroyed a LinkedList\n\0", 0x17)      = 23 0

LinkedList.cpp

#include <iostream>
#include "LinkedList.h"

using namespace std;

LinkedList::LinkedList() {
    this->length = 0;
    this->head = NULL;
    this->tail = NULL;
    cout << "Created new LinkedList" << endl;
}

LinkedList::~LinkedList() {
    Node* curr;
    Node* temp;
    curr = this->head;

    while ( curr ) {
        temp = curr;
        curr = curr->next;
        delete temp;
    }

    cout << "Destroyed a LinkedList" << endl;
}

void LinkedList::Add(int v) {

    Node* n = new Node();
    n->val = v;
    n->next = NULL;

    if (!this->head) {
        this->head = n;
        this->tail = n;
    } else {
        this->tail->next = n;
        this->tail = n;
    }    
}

main.cpp

#include <iostream>
#include "LinkedList.h"

using namespace std;

int main() {

    LinkedList l; // You should require a heap increase, right?

    LinkedList* ll = new LinkedList(); // Surely you require more heap!

    for (int i=0; i<1000; i++) 
        l.Add(i);

    return 0;

}

Solution

  • I found that Mac OS doesn't use sbrk/brk/break() for memory management like most UNIX/Linuxes. Basically, it's using the Mach kernel that Apple inherited from NeXT so the memory calls are going to be mpadvise(2) and mprotect(2) which offer finer grain control than sbrk().

    From "Mac OS X and iOS Internals" by Jonathan Levin:

    enter image description here

    So to interpret your memory allocations you'd need to know the mprotect(2) arguments from the sys/mman.h header.

    #define PROT_NONE       0x00    /* [MC2] no permissions */
    #define PROT_READ       0x01    /* [MC2] pages can be read */
    #define PROT_WRITE      0x02    /* [MC2] pages can be written */
    #define PROT_EXEC       0x04    /* [MC2] pages can be executed */
    ....
    

    And so your system calls would imply:

    mprotect(0x10C9D0000, 0x1000, 0x0)       = 0 0
    mprotect(0x10C9D5000, 0x1000, 0x0)       = 0 0
    mprotect(0x10C9D6000, 0x1000, 0x0)       = 0 0
    mprotect(0x10C9DB000, 0x1000, 0x0)       = 0 0
    mprotect(0x10C9CE000, 0x88, 0x1)         = 0 0  <-- Allow reads starting at 0x10C9CE000 for 136 bytes
    mprotect(0x10C9DC000, 0x1000, 0x1)       = 0 0
    mprotect(0x10C9CE000, 0x88, 0x3)         = 0 0  <-- Allow reads and writes starting at 0x10C9CE000 for 136 bytes
    mprotect(0x10C9CE000, 0x88, 0x1)         = 0 0
    

    Regarding mmap(2), on Linux systems it's used to map the object code of the shared libraries but not used for malloc/free or new/delete.

    References: