Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DTrace support for OS X #6

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions dtracetool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
DTrace Memory Tracer
====================

This is a DTrace tool which aims to create an ltrace compatible output for
heap management functions which can be visualised by villoc.

Tested only on OS X. Other supported platforms may require modificatons.

Usage
-----

```shell
$ sudo ./memtrace.d -c <app> | ./villoc.py - out.html
```

149 changes: 149 additions & 0 deletions dtracetool/memtrace.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/usr/sbin/dtrace -s

/*
memtrace.d - DTrace-based memory allocation tracer
Andrzej Dyjak <[email protected]>

Based on ltrace output style. Compatible with villoc [1].

$ sudo dtrace ./memtrace.d -p <pid>
$ sudo dtrace ./memtrace.d -c <application>

NOTE: For bigger programs you may stumble upon data drops. This can be mitigated
to a certain degree via DTrace tuning [2] [3].

[1] https://github.com/wapiflapi/villoc
[2] https://wikis.oracle.com/display/DTrace/Buffers+and+Buffering
[3] https://wikis.oracle.com/display/DTrace/Options+and+Tunables
*/

/* Re-enable to see potential data drops */
#pragma D option quiet

/* Globals for alloc functions args. Explicitly typed to avoid errors. */
size_t malloc_size;
size_t valloc_size;
size_t calloc_count;
size_t calloc_size;
int64_t realloc_addr;
size_t realloc_size;
int64_t reallocf_addr;
size_t reallocf_size;
int64_t free_addr;

BEGIN
{
/* Flags for failure inside of the functions */
/* However, we're using them also for predicates. This is a hack. */
malloc_fail = 0;
valloc_fail = 0;
calloc_fail = 0;
realloc_fail = 0;
reallocf_fail = 0;
free_fail = 0;
}


pid$target::malloc:entry
{
malloc_size = arg0;
malloc_fail = 1;
}

pid$target::malloc:return
/malloc_fail/
{
printf("malloc(%d) = %#p\n", malloc_size, arg1);
malloc_fail = 0;
}

pid$target::valloc:entry
{
valloc_size = arg0;
valloc_fail = 1;
}

pid$target::valloc:return
/valloc_fail/
{
printf("valloc(%d) = %#p\n", valloc_size, arg1);
valloc_fail = 0;
}

pid$target::calloc:entry
{
calloc_count = arg0;
calloc_size = arg1;
calloc_fail = 1;
}

pid$target::calloc:return
/calloc_fail/
{
printf("calloc(%d, %d) = %#p\n", calloc_count, calloc_size, arg1);
calloc_fail = 0;
}

pid$target::realloc:entry
{
realloc_addr = arg0;
realloc_size = arg1;
realloc_fail = 1;
}

pid$target::realloc:return
/realloc_fail/
{
printf("realloc(%#p, %d) = %#p\n", realloc_addr, realloc_size, arg1);
realloc_fail = 0;
}

pid$target::reallocf:entry
{
reallocf_addr = arg0;
reallocf_size = arg1;
reallocf_fail = 1;
}

pid$target::reallocf:return
/reallocf_fail/
{
printf("reallocf(%#p, %d) = %#p\n", reallocf_addr, reallocf_size, arg1);
reallocf_fail = 0;
}

pid$target::free:entry
{
free_addr = arg0;
printf("free(%#p) = <void>\n", free_addr);
}

END
/malloc_fail/
{
printf("malloc(%d) = <error>\n", malloc_size);
}

END
/valloc_fail/
{
printf("valloc(%d) = <error>\n", valloc_size);
}

END
/calloc_fail/
{
printf("calloc(%d, %d) = <error>\n", calloc_count, calloc_size);
}

END
/realloc_fail/
{
printf("realloc(%#p, %d) = <error>\n", realloc_addr, realloc_size);
}

END
/reallocf_fail/
{
printf("realloc(%#p, %d) = <error>\n", reallocf_addr, reallocf_size);
}
17 changes: 17 additions & 0 deletions dtracetool/tests/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
CC = clang

all: test1 test2 test3

test1: test1.c
$(CC) -o test1 test1.c

test2: test2.c
$(CC) -o test2 test2.c

test3: test3.c
$(CC) -o test3 test3.c

clean:
rm test1
rm test2
rm test3
39 changes: 39 additions & 0 deletions dtracetool/tests/test1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* Testing for each alloc function from libc */

#include <stdio.h>
#include <stdlib.h>

#define DEBUG 0

int
main(void)
{
int *cptr, *mptr, *vptr;

cptr = calloc(8, 32);
if(DEBUG)
printf("cptr = %p\n", cptr);

mptr = malloc(64);
if(DEBUG)
printf("mptr = %p\n", mptr);

mptr = realloc(mptr, 128);
if(DEBUG)
printf("mptr = %p\n", mptr);

vptr = valloc(64);
if(DEBUG)
printf("vptr = %p\n", vptr);

vptr = reallocf(vptr, 128);
if(DEBUG)
printf("vptr = %p\n", vptr);

free(cptr);
free(mptr);
free(vptr);

return 0;
}

69 changes: 69 additions & 0 deletions dtracetool/tests/test2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/* Testing for fail/success of alloc functions */

#include <stdio.h>
#include <stdlib.h>

#define DEBUG 0

int
main(void)
{
int *cptr, *mptr, *vptr, *rptr, *rfptr;

/* Failing calloc */
cptr = calloc(1337, 0xFFFFFFFFFFFF);
if(DEBUG)
printf("cptr = %p\n", cptr);

/* Succeeding calloc */
cptr = calloc(8, 32);
if(DEBUG)
printf("cptr = %p\n", cptr);

/* Failing malloc */
mptr = malloc(0xFFFFFFFFFFFFFF);
if(DEBUG)
printf("mptr = %p\n", mptr);

/* Succeeding malloc */
mptr = malloc(64);
if(DEBUG)
printf("mptr = %p\n", mptr);

/* Failing realloc, we want to test if mptr is still valid */
rptr = realloc(mptr, 0xFFFFFFFFFFFF);
if(DEBUG)
printf("mptr = %p\n", mptr);

/* Succeeding realloc */
mptr = realloc(mptr, 128);
if(DEBUG)
printf("mptr = %p\n", mptr);

/* Failing valloc */
vptr = valloc(0xFFFFFFFFFFFF);
if(DEBUG)
printf("vptr = %p\n", vptr);

/* Succeeding valloc */
vptr = valloc(64);
if(DEBUG)
printf("vptr = %p\n", vptr);

/* Succeeding reallocf */
vptr = reallocf(vptr, 128);
if(DEBUG)
printf("vptr = %p\n", vptr);

/* Failing reallocf, we want to test if vptr is still valid */
rfptr = reallocf(vptr, 0xFFFFFFFFFFFF);
if(DEBUG)
printf("vptr = %p\n", vptr);

free(cptr);
free(mptr);
// free(vptr);

return 0;
}

34 changes: 34 additions & 0 deletions dtracetool/tests/test3.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* Testing for a crash inside of the malloc() */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main(int argc, char **argv)
{
(void) argc, (void) argv;

void *a, *b, *c;

a = malloc(48);
b = malloc(48);
c = malloc(48);

free(a);

// Let's say we have an underflow on c. This
// is far from the only way to trigger a crash.
memset(c-128, 0xff, 0x200);

printf("A crash in malloc will be triggered *after* this print.\n");
malloc(48);

printf("This shouldn't be reached but it is on OS X (unlike Ubuntu)\n");
malloc(48);

printf("However, this isn't reached\n");
malloc(1337);

return 0;
}

17 changes: 17 additions & 0 deletions villoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ def malloc(state, ret, size):
state.append(Block(ret, size))


def valloc(state, ret, size):
malloc(state, ret, size)


def calloc(state, ret, nmemb, size):
malloc(state, ret, nmemb * size)

Expand All @@ -183,6 +187,8 @@ def realloc(state, ret, ptr, size):

if not ptr:
return malloc(state, ret, size)
elif not ret:
return malloc(state, ret, size)
elif not size:
return free(state, ret, ptr)

Expand All @@ -195,19 +201,30 @@ def realloc(state, ret, ptr, size):
state[s].error = True
else:
state[s] = Block(ret, size, color=match.color)


# This is just an empty stub for reallocf(). This is because internally
# reallocf() calls realloc() so we catch it there.
# However, for the sake of completness it's good to have it in the output.
def reallocf(state, ret, ptr, size):
return


operations = {
'free': free,
'malloc': malloc,
'valloc': valloc,
'calloc': calloc,
'realloc': realloc,
'reallocf': reallocf,
}


def sanitize(x):
if x is None:
return None
if x == "<error>":
return None
if x == "<void>":
return 0
return int(x, 0)
Expand Down