mirror of https://github.com/svaarala/duktape.git
Browse Source
Add two example allocators to the distributable: - A logging allocator writes alloc/realloc/free sizes and results to /tmp/duk-alloc-log.txt. The log includes previous allocation size for realloc() and free() so that the memory behavior can be played back and used for e.g. optimizing allocator pool sizes. - A torture allocator uses red zones to detect out-of-bound writes, inits data to a non-zero value, and wipes freed memory areas so accidental reads after freeing are detected more easily. This allocator can be used on almost any target to detect basic issues and is useful especially when Valgrind doesn't work on the target. Also fix some NULL checks in sandbox allocator example.weak-references
Sami Vaarala
10 years ago
13 changed files with 489 additions and 15 deletions
@ -0,0 +1,7 @@ |
|||
====================== |
|||
Allocator with logging |
|||
====================== |
|||
|
|||
Example allocator that writes all memory alloc/realloc/free calls into a |
|||
log file so that memory usage can replayed later. This is useful to e.g. |
|||
optimize pool sizes. |
@ -0,0 +1,138 @@ |
|||
/*
|
|||
* Example memory allocator with machine parseable logging. |
|||
* |
|||
* Also sizes for reallocs and frees are logged so that the memory |
|||
* behavior can be essentially replayed to accurately determine e.g. |
|||
* optimal pool sizes for a pooled allocator. |
|||
* |
|||
* Allocation structure: |
|||
* |
|||
* [ alloc_hdr | user area ] |
|||
* |
|||
* ^ ^ |
|||
* | `--- pointer returned to Duktape |
|||
* `--- underlying malloc ptr |
|||
*/ |
|||
|
|||
#include "duktape.h" |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <stdint.h> |
|||
|
|||
#define ALLOC_LOG_FILE "/tmp/duk-alloc-log.txt" |
|||
|
|||
typedef struct { |
|||
/* The double value in the union is there to ensure alignment is
|
|||
* good for IEEE doubles too. In many 32-bit environments 4 bytes |
|||
* would be sufficiently aligned and the double value is unnecessary. |
|||
*/ |
|||
union { |
|||
size_t sz; |
|||
double d; |
|||
} u; |
|||
} alloc_hdr; |
|||
|
|||
static FILE *log_file = NULL; |
|||
|
|||
static void write_log(const char *fmt, ...) { |
|||
va_list ap; |
|||
|
|||
if (!log_file) { |
|||
log_file = fopen(ALLOC_LOG_FILE, "wb"); |
|||
if (!log_file) { |
|||
return; |
|||
} |
|||
} |
|||
|
|||
va_start(ap, fmt); |
|||
vfprintf(log_file, fmt, ap); |
|||
va_end(ap); |
|||
} |
|||
|
|||
void *duk_alloc_logging(void *udata, duk_size_t size) { |
|||
alloc_hdr *hdr; |
|||
void *ret; |
|||
|
|||
(void) udata; /* Suppress warning. */ |
|||
|
|||
if (size == 0) { |
|||
write_log("A NULL %ld\n", (long) size); |
|||
return NULL; |
|||
} |
|||
|
|||
hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr)); |
|||
if (!hdr) { |
|||
write_log("A FAIL %ld\n", (long) size); |
|||
return NULL; |
|||
} |
|||
hdr->u.sz = size; |
|||
ret = (void *) (hdr + 1); |
|||
write_log("A %p %ld\n", ret, (long) size); |
|||
return ret; |
|||
} |
|||
|
|||
void *duk_realloc_logging(void *udata, void *ptr, duk_size_t size) { |
|||
alloc_hdr *hdr; |
|||
size_t old_size; |
|||
void *t; |
|||
void *ret; |
|||
|
|||
(void) udata; /* Suppress warning. */ |
|||
|
|||
/* Handle the ptr-NULL vs. size-zero cases explicitly to minimize
|
|||
* platform assumptions. You can get away with much less in specific |
|||
* well-behaving environments. |
|||
*/ |
|||
|
|||
if (ptr) { |
|||
hdr = (alloc_hdr *) ((unsigned char *) ptr - sizeof(alloc_hdr)); |
|||
old_size = hdr->u.sz; |
|||
|
|||
if (size == 0) { |
|||
free((void *) hdr); |
|||
write_log("R %p %ld NULL 0\n", ptr, (long) old_size); |
|||
return NULL; |
|||
} else { |
|||
t = realloc((void *) hdr, size + sizeof(alloc_hdr)); |
|||
if (!t) { |
|||
write_log("R %p %ld FAIL %ld\n", ptr, (long) old_size, (long) size); |
|||
return NULL; |
|||
} |
|||
hdr = (alloc_hdr *) t; |
|||
hdr->u.sz = size; |
|||
ret = (void *) (hdr + 1); |
|||
write_log("R %p %ld %p %ld\n", ptr, (long) old_size, ret, (long) size); |
|||
return ret; |
|||
} |
|||
} else { |
|||
if (size == 0) { |
|||
write_log("R NULL 0 NULL 0\n"); |
|||
return NULL; |
|||
} else { |
|||
hdr = (alloc_hdr *) malloc(size + sizeof(alloc_hdr)); |
|||
if (!hdr) { |
|||
write_log("R NULL 0 FAIL %ld\n", (long) size); |
|||
return NULL; |
|||
} |
|||
hdr->u.sz = size; |
|||
ret = (void *) (hdr + 1); |
|||
write_log("R NULL 0 %p %ld\n", ret, (long) size); |
|||
return ret; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void duk_free_logging(void *udata, void *ptr) { |
|||
alloc_hdr *hdr; |
|||
|
|||
(void) udata; /* Suppress warning. */ |
|||
|
|||
if (!ptr) { |
|||
write_log("F NULL 0\n"); |
|||
return; |
|||
} |
|||
hdr = (alloc_hdr *) ((unsigned char *) ptr - sizeof(alloc_hdr)); |
|||
write_log("F %p %ld\n", ptr, (long) hdr->u.sz); |
|||
free((void *) hdr); |
|||
} |
@ -0,0 +1,10 @@ |
|||
#ifndef DUK_ALLOC_LOGGING_H_INCLUDED |
|||
#define DUK_ALLOC_LOGGING_H_INCLUDED |
|||
|
|||
#include "duktape.h" |
|||
|
|||
void *duk_alloc_logging(void *udata, duk_size_t size); |
|||
void *duk_realloc_logging(void *udata, void *ptr, duk_size_t size); |
|||
void duk_free_logging(void *udata, void *ptr); |
|||
|
|||
#endif /* DUK_ALLOC_LOGGING_H_INCLUDED */ |
@ -0,0 +1,39 @@ |
|||
#!/usr/bin/python |
|||
# |
|||
# Analyze allocator logs and write total-bytes-in-use after every |
|||
# operation to stdout. The output can be gnuplotted as: |
|||
# |
|||
# $ python log2gnuplot.py </tmp/duk-alloc-log.txt >/tmp/output.txt |
|||
# $ gnuplot |
|||
# > plot "output.txt" with lines |
|||
# |
|||
|
|||
import os |
|||
import sys |
|||
|
|||
def main(): |
|||
allocated = 0 |
|||
|
|||
for line in sys.stdin: |
|||
line = line.strip() |
|||
parts = line.split(' ') |
|||
|
|||
# A ptr/NULL/FAIL size |
|||
# F ptr/NULL size |
|||
# R ptr/NULL oldsize ptr/NULL/FAIL newsize |
|||
|
|||
if parts[0] == 'A': |
|||
if parts[1] != 'NULL' and parts[1] != 'FAIL': |
|||
allocated += long(parts[2]) |
|||
elif parts[0] == 'F': |
|||
allocated -= long(parts[2]) |
|||
elif parts[0] == 'R': |
|||
allocated -= long(parts[2]) |
|||
if parts[3] != 'NULL' and parts[3] != 'FAIL': |
|||
allocated += long(parts[4]) |
|||
print(allocated) |
|||
|
|||
print(allocated) |
|||
|
|||
if __name__ == '__main__': |
|||
main() |
@ -0,0 +1,10 @@ |
|||
========================================== |
|||
Allocator with memory wiping and red zones |
|||
========================================== |
|||
|
|||
Example allocator that wipes memory on free and checks that no out-of-bounds |
|||
writes have been made to bytes just before and after the allocated area. |
|||
|
|||
Valgrind is a better tool for detecting these memory issues, but it's not |
|||
available for all targets so you can use something like this to detect |
|||
memory lifecycle or out-of-bounds issues. |
@ -0,0 +1,182 @@ |
|||
/*
|
|||
* Example torture memory allocator with memory wiping and check for |
|||
* out-of-bounds writes. |
|||
* |
|||
* Allocation structure: |
|||
* |
|||
* [ alloc_hdr | red zone before | user area | red zone after ] |
|||
* |
|||
* ^ ^ |
|||
* | `--- pointer returned to Duktape |
|||
* `--- underlying malloc ptr |
|||
*/ |
|||
|
|||
#include "duktape.h" |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <stdint.h> |
|||
|
|||
#define RED_ZONE_SIZE 16 |
|||
#define RED_ZONE_BYTE 0x5a |
|||
#define INIT_BYTE 0xa5 |
|||
#define WIPE_BYTE 0x27 |
|||
|
|||
typedef struct { |
|||
/* The double value in the union is there to ensure alignment is
|
|||
* good for IEEE doubles too. In many 32-bit environments 4 bytes |
|||
* would be sufficiently aligned and the double value is unnecessary. |
|||
*/ |
|||
union { |
|||
size_t sz; |
|||
double d; |
|||
} u; |
|||
} alloc_hdr; |
|||
|
|||
static void check_red_zone(alloc_hdr *hdr) { |
|||
size_t size; |
|||
int i; |
|||
int err; |
|||
unsigned char *p; |
|||
unsigned char *userptr; |
|||
|
|||
size = hdr->u.sz; |
|||
userptr = (unsigned char *) hdr + sizeof(alloc_hdr) + RED_ZONE_SIZE; |
|||
|
|||
err = 0; |
|||
p = (unsigned char *) hdr + sizeof(alloc_hdr); |
|||
for (i = 0; i < RED_ZONE_SIZE; i++) { |
|||
if (p[i] != RED_ZONE_BYTE) { |
|||
err = 1; |
|||
} |
|||
} |
|||
if (err) { |
|||
fprintf(stderr, "RED ZONE CORRUPTED BEFORE ALLOC: hdr=%p ptr=%p size=%ld\n", |
|||
(void *) hdr, (void *) userptr, (long) size); |
|||
fflush(stderr); |
|||
} |
|||
|
|||
err = 0; |
|||
p = (unsigned char *) hdr + sizeof(alloc_hdr) + RED_ZONE_SIZE + size; |
|||
for (i = 0; i < RED_ZONE_SIZE; i++) { |
|||
if (p[i] != RED_ZONE_BYTE) { |
|||
err = 1; |
|||
} |
|||
} |
|||
if (err) { |
|||
fprintf(stderr, "RED ZONE CORRUPTED AFTER ALLOC: hdr=%p ptr=%p size=%ld\n", |
|||
(void *) hdr, (void *) userptr, (long) size); |
|||
fflush(stderr); |
|||
} |
|||
} |
|||
|
|||
void *duk_alloc_torture(void *udata, duk_size_t size) { |
|||
unsigned char *p; |
|||
|
|||
(void) udata; /* Suppress warning. */ |
|||
|
|||
if (size == 0) { |
|||
return NULL; |
|||
} |
|||
|
|||
p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
|||
if (!p) { |
|||
return NULL; |
|||
} |
|||
|
|||
((alloc_hdr *) p)->u.sz = size; |
|||
p += sizeof(alloc_hdr); |
|||
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
|||
p += RED_ZONE_SIZE; |
|||
memset((void *) p, INIT_BYTE, size); |
|||
p += size; |
|||
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
|||
p -= size; |
|||
return (void *) p; |
|||
} |
|||
|
|||
void *duk_realloc_torture(void *udata, void *ptr, duk_size_t size) { |
|||
unsigned char *p, *old_p; |
|||
size_t old_size; |
|||
|
|||
(void) udata; /* Suppress warning. */ |
|||
|
|||
/* Handle the ptr-NULL vs. size-zero cases explicitly to minimize
|
|||
* platform assumptions. You can get away with much less in specific |
|||
* well-behaving environments. |
|||
*/ |
|||
|
|||
if (ptr) { |
|||
old_p = (unsigned char *) ptr - sizeof(alloc_hdr) - RED_ZONE_SIZE; |
|||
old_size = ((alloc_hdr *) old_p)->u.sz; |
|||
check_red_zone((alloc_hdr *) old_p); |
|||
|
|||
if (size == 0) { |
|||
memset((void *) old_p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
|||
free((void *) old_p); |
|||
return NULL; |
|||
} else { |
|||
/* Force address change on every realloc. */ |
|||
p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
|||
if (!p) { |
|||
return NULL; |
|||
} |
|||
|
|||
((alloc_hdr *) p)->u.sz = size; |
|||
p += sizeof(alloc_hdr); |
|||
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
|||
p += RED_ZONE_SIZE; |
|||
if (size > old_size) { |
|||
memcpy((void *) p, (void *) (old_p + sizeof(alloc_hdr) + RED_ZONE_SIZE), old_size); |
|||
memset((void *) (p + old_size), INIT_BYTE, size - old_size); |
|||
} else { |
|||
memcpy((void *) p, (void *) (old_p + sizeof(alloc_hdr) + RED_ZONE_SIZE), size); |
|||
} |
|||
p += size; |
|||
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
|||
p -= size; |
|||
|
|||
memset((void *) old_p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
|||
free((void *) old_p); |
|||
|
|||
return (void *) p; |
|||
} |
|||
} else { |
|||
if (size == 0) { |
|||
return NULL; |
|||
} else { |
|||
p = (unsigned char *) malloc(size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
|||
if (!p) { |
|||
return NULL; |
|||
} |
|||
|
|||
((alloc_hdr *) p)->u.sz = size; |
|||
p += sizeof(alloc_hdr); |
|||
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
|||
p += RED_ZONE_SIZE; |
|||
memset((void *) p, INIT_BYTE, size); |
|||
p += size; |
|||
memset((void *) p, RED_ZONE_BYTE, RED_ZONE_SIZE); |
|||
p -= size; |
|||
return (void *) p; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void duk_free_torture(void *udata, void *ptr) { |
|||
unsigned char *p; |
|||
size_t old_size; |
|||
|
|||
(void) udata; /* Suppress warning. */ |
|||
|
|||
if (!ptr) { |
|||
return; |
|||
} |
|||
|
|||
p = (unsigned char *) ptr - sizeof(alloc_hdr) - RED_ZONE_SIZE; |
|||
old_size = ((alloc_hdr *) p)->u.sz; |
|||
|
|||
check_red_zone((alloc_hdr *) p); |
|||
memset((void *) p, WIPE_BYTE, old_size + sizeof(alloc_hdr) + 2 * RED_ZONE_SIZE); |
|||
free((void *) p); |
|||
} |
@ -0,0 +1,10 @@ |
|||
#ifndef DUK_ALLOC_TORTURE_H_INCLUDED |
|||
#define DUK_ALLOC_TORTURE_H_INCLUDED |
|||
|
|||
#include "duktape.h" |
|||
|
|||
void *duk_alloc_torture(void *udata, duk_size_t size); |
|||
void *duk_realloc_torture(void *udata, void *ptr, duk_size_t size); |
|||
void duk_free_torture(void *udata, void *ptr); |
|||
|
|||
#endif /* DUK_ALLOC_TORTURE_H_INCLUDED */ |
Loading…
Reference in new issue