Dtruss not showing mmap/sbrk calls?
Asked Answered
E

1

7

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;

}
Elongate answered 29/3, 2018 at 21:30 Comment(4)
Is this the full dtrace output?Dissimilitude
If you increase the size of your linked list, by say an order of magnitude, do you see the same behavior?Philender
Yes - this is the full DTRUSS output. No - increasing the size of the list significantly (e.g. Gigabytes worth of data) doesn't make a difference, although notably there are more calls to madvise.Elongate
Does dtruss show those calls for other processes?Colwen
D
5

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:

Dissimilitude answered 13/4, 2018 at 23:35 Comment(1)
In addition, sbrk() is at best legacy and not very good legacy at that. The FreeBSD manpage says: "The brk() and sbrk() functions are legacy interfaces from before the advent of modern virtual memory management." Read that as V7 Unix. Moreover, the FreeBSD 11.0 arm64 and riscv architectures have dropped support for sbrk() altogether.Meganmeganthropus

© 2022 - 2024 — McMap. All rights reserved.