Permalink
Browse files

memory allocation rework

Courtesy of Leopold Toetsch <lt@toetsch.at>


git-svn-id: https://svn.parrot.org/parrot/trunk@2348 d31e2699-5ff4-0310-a27c-f18f2fbe73fe
  • Loading branch information...
1 parent 1ccf407 commit c0142c7e3c570aa298a3afd3404dfa6804e961ae Dan Sugalski committed Oct 5, 2002
Showing with 12,147 additions and 60 deletions.
  1. +4 −0 Configure.pl
  2. +5 −0 MANIFEST
  3. +67 −0 config/auto/gc.pl
  4. +15 −0 config/auto/gc/test_c.in
  5. +5 −2 config/gen/makefiles/root.in
  6. +120 −0 dod.c
  7. +20 −0 headers.c
  8. +1 −0 lib/Parrot/Configure/RunSteps.pm
  9. +215 −0 malloc-trace.c
  10. +5,567 −0 malloc.c
  11. +85 −0 res_lea.c
  12. +120 −0 src/dod.c
  13. +20 −0 src/headers.c
  14. +215 −0 src/malloc-trace.c
  15. +5,567 −0 src/malloc.c
  16. +85 −0 src/res_lea.c
  17. +18 −29 src/string.c
  18. +18 −29 string.c
View
4 Configure.pl
@@ -57,6 +57,10 @@ END
--ld=(linker) Use the given linker
--intval=(type) Use the given type for INTVAL
--floatval=(type) Use the given type for FLOATVAL
+
+ --cgoto=0 Don't build cgoto core - recommended when short of mem
+ --gc=gc|libc|malloc|malloc-trace determine GC type, default = gc
+
EOT
exit;
};
View
5 MANIFEST
@@ -45,6 +45,8 @@ config/auto/cgoto/test_c.in
config/auto/format.pl
config/auto/funcptr.pl
config/auto/funcptr/test_c.in
+config/auto/gc.pl
+config/auto/gc/test_c.in
config/auto/gcc.pl
config/auto/gcc/test_c.in
config/auto/headers.pl
@@ -554,6 +556,8 @@ lib/Test/More.pm
lib/Test/Simple.pm
lib/Text/Balanced.pm
make.pl
+malloc.c
+malloc-trace.c
math.ops
memory.c
method_util.c
@@ -573,6 +577,7 @@ pdump.c
pmc.c
pxs.c
register.c
+res_lea.c
resources.c
runops_cores.c
rx.c
View
67 config/auto/gc.pl
@@ -0,0 +1,67 @@
+package Configure::Step;
+
+use strict;
+use vars qw($description @args);
+use Parrot::Configure::Step ':auto';
+
+$description="Determining what allocator to use ...";
+
+# valid libc/malloc/malloc-trace/gc
+@args=qw(gc);
+
+sub runstep {
+ my ($gc) = @_;
+
+ if (!defined($gc)) {
+ # default is GC in resources.c
+ $gc = 'gc';
+ }
+ elsif ($gc eq 'libc') {
+ # tests mallinfo after allocation of 128 bytes
+ cc_gen('config/auto/gc/test_c.in');
+ eval { cc_build(); };
+ my $test = 0;
+ unless ($@) {
+ $test = cc_run();
+ }
+ cc_clean();
+ # used size should be somewhere here
+ unless ($test >= 128 && $test < 155) {
+ # if not, use own copy of malloc
+ $gc = 'malloc';
+ }
+ }
+
+ if ($gc =~ /^malloc(?:-trace)?$/) {
+ Configure::Data->set(
+ gc_c => <<"EOF",
+$gc\$(O): \$(GENERAL_H_FILES) $gc.c
+res_lea\$(O): \$(GENERAL_H_FILES) res_lea.c
+EOF
+ gc_o => "$gc\$(O) res_lea\$(O)",
+ gc_flag => '-DGC_IS_MALLOC',
+ );
+ }
+ elsif ($gc eq 'libc') {
+ Configure::Data->set(
+ gc_c => <<"EOF",
+res_lea\$(O): \$(GENERAL_H_FILES) res_lea.c
+EOF
+ gc_o => "res_lea\$(O)",
+ gc_flag => '-DGC_IS_MALLOC',
+ );
+ }
+ else {
+ $gc = 'gc';
+ Configure::Data->set(
+ gc_c => <<"EOF",
+resources\$(O): \$(GENERAL_H_FILES) resources.c
+EOF
+ gc_o => "resources\$(O)",
+ gc_flag => '',
+ );
+ }
+ print(" ($gc) ");
+}
+
+1;
View
15 config/auto/gc/test_c.in
@@ -0,0 +1,15 @@
+/*
+ * gc.c - figure out if we can use malloc as allocator
+ *
+ * This file is automatically generated by Configure
+ * from gc.in.
+ */
+#include <malloc.h>
+#include <stdio.h>
+int main(int argc, char **argv) {
+ void *p = malloc(128);
+ int used = mallinfo().uordblks;
+ printf("%d\n", used);
+
+ return 0;
+}
View
7 config/gen/makefiles/root.in
@@ -96,7 +96,7 @@ INTERP_O_FILES = exceptions$(O) global_setup$(O) interpreter$(O) parrot$(O) \
packfile$(O) stacks$(O) string$(O) sub$(O) encoding$(O) \
chartype$(O) runops_cores$(O) trace$(O) pmc$(O) key$(O) hash$(O) \
core_pmcs$(O) platform$(O) ${jit_o} \
- resources$(O) rx$(O) rxstacks$(O) intlist$(O) \
+ ${gc_o} rx$(O) rxstacks$(O) intlist$(O) \
embed$(O) warnings$(O) misc$(O) ${cg_o} \
packout$(O) byteorder$(O) debug$(O) smallobject$(O) \
headers$(O) dod$(O) method_util$(O)
@@ -119,7 +119,8 @@ OPS_FILES = ${ops} $(GEN_OPSFILES)
#
###############################################################################
-CFLAGS = ${ccflags} ${cc_warn} ${cc_inc} ${cc_hasjit} ${cg_flag}
+CFLAGS = ${ccflags} ${cc_warn} ${cc_inc} ${cc_hasjit} ${cg_flag} ${gc_flag}
+
LINKFLAGS = ${linkflags}
LDFLAGS = ${ldflags}
@@ -415,6 +416,8 @@ core_ops_prederef.c $(INC)/oplib/core_ops_prederef.h : $(OPS_FILES) ops2c.pl lib
${cg_c}
+${gc_c}
+
warnings$(O) : $(H_FILES)
misc$(O) : $(H_FILES)
View
120 dod.c
@@ -308,6 +308,57 @@ free_unused_PMCs(struct Parrot_Interp *interpreter)
interpreter->arena_base->pmc_pool->total_objects - total_used;
}
+#ifdef GC_IS_MALLOC
+
+/* find other users of COW's bufstart */
+static void
+used_cow(struct Parrot_Interp *interpreter, struct Small_Object_Pool *pool)
+{
+ UINTVAL object_size = pool->object_size;
+ struct Small_Object_Arena *cur_arena;
+ UINTVAL i;
+ Buffer *b;
+ char *tail;
+
+#ifdef LEA_DEBUG
+ /* check/clear tail, e.g. on changes in string.c or res.c */
+ for (cur_arena = pool->last_Arena;
+ NULL != cur_arena;
+ cur_arena = cur_arena->prev) {
+ b = cur_arena->start_objects;
+ for (i = 0; i < cur_arena->used; i++) {
+ if ((b->flags & BUFFER_COW_FLAG) && b->bufstart &&
+ !(b->flags & BUFFER_external_FLAG)) {
+ tail = (char*)b->bufstart + b->buflen + 1;
+ assert(*tail == 0);
+ *tail = 0;
+ }
+ b = (Buffer *)((char *)b + object_size);
+ }
+ }
+#endif
+
+ for (cur_arena = pool->last_Arena;
+ NULL != cur_arena;
+ cur_arena = cur_arena->prev) {
+ b = cur_arena->start_objects;
+ for (i = 0; i < cur_arena->used; i++) {
+ if ((b->flags & BUFFER_COW_FLAG) &&
+ !(b->flags & BUFFER_external_FLAG)) {
+ tail = (char*)b->bufstart + b->buflen + 1;
+ /* mark living and dead users of this bufstart
+ * tail is cleared in *allocate_string */
+ if (b->flags & BUFFER_live_FLAG)
+ *tail |= 0x2;
+ else
+ *tail |= 0x1;
+ }
+ b = (Buffer *)((char *)b + object_size);
+ }
+ }
+}
+
+#endif /* GC_IS_MALLOC */
/* Put any buffers that are now unused, on to the free list
* Avoid buffers that are immune from collection (ie, constant) */
static void
@@ -317,13 +368,26 @@ free_unused_buffers(struct Parrot_Interp *interpreter,
struct Small_Object_Arena *cur_arena;
UINTVAL i, total_used = 0;
UINTVAL object_size = pool->object_size;
+#ifdef GC_IS_MALLOC
+ char *tail;
+#endif /* GC_IS_MALLOC */
+#ifdef GC_IS_MALLOC
+ used_cow(interpreter, pool);
+#endif /* GC_IS_MALLOC */
/* Run through all the buffer header pools and mark */
for (cur_arena = pool->last_Arena;
NULL != cur_arena;
cur_arena = cur_arena->prev) {
Buffer *b = cur_arena->start_objects;
for (i = 0; i < cur_arena->used; i++) {
+#ifdef GC_IS_MALLOC
+ if ((b->flags & BUFFER_COW_FLAG) &&
+ !(b->flags & BUFFER_external_FLAG))
+ tail = (char*)b->bufstart + b->buflen + 1;
+ else
+ tail = 0;
+#endif /* GC_IS_MALLOC */
/* If it's not live or on the free list, put it on the free list.
* Note that it is technically possible to have a Buffer be both
* on_free_list and live, because of our conservative stack-walk
@@ -332,6 +396,7 @@ free_unused_buffers(struct Parrot_Interp *interpreter,
| BUFFER_constant_FLAG
| BUFFER_live_FLAG )))
{
+#ifndef GC_IS_MALLOC
if (pool->mem_pool) {
if (!(b->flags & BUFFER_COW_FLAG)) {
((struct Memory_Pool *)
@@ -341,10 +406,27 @@ free_unused_buffers(struct Parrot_Interp *interpreter,
((struct Memory_Pool *)
pool->mem_pool)->possibly_reclaimable += b->buflen;
}
+#else /* GC_IS_MALLOC */
+ /* if only dead users, this one may be freed */
+ if (tail && *tail == 0x1)
+ b->flags &= ~BUFFER_COW_FLAG;
+ /* don't free this bufstart this time, because
+ * tail will be invalid then -
+ * if we want to free immediately, we need extended
+ * bookkeeping to free exactly the last user
+ */
+ else
+#endif /* GC_IS_MALLOC */
add_free_buffer(interpreter, pool, b);
} else if (!(b->flags & BUFFER_on_free_list_FLAG)) {
total_used++;
}
+#ifdef GC_IS_MALLOC
+ /* clear tail for next dod run and
+ * don't unset COW on other possbily dead users */
+ if (tail)
+ *tail = 0;
+#endif /* GC_IS_MALLOC */
b->flags &= ~BUFFER_live_FLAG;
b = (Buffer *)((char *)b + object_size);
}
@@ -429,11 +511,31 @@ trace_system_stack(struct Parrot_Interp *interpreter, PMC *last)
}
#endif
+#ifdef GC_IS_MALLOC
+struct mallinfo {
+ int arena; /* non-mmapped space allocated from system */
+ int ordblks; /* number of free chunks */
+ int smblks; /* number of fastbin blocks */
+ int hblks; /* number of mmapped regions */
+ int hblkhd; /* space in mmapped regions */
+ int usmblks; /* maximum total allocated space */
+ int fsmblks; /* space available in freed fastbin blocks */
+ int uordblks; /* total allocated space */
+ int fordblks; /* total free space */
+ int keepcost; /* top-most, releasable (via malloc_trim) space */
+};
+extern struct mallinfo mallinfo(void);
+#endif /* GC_IS_MALLOC */
/* See if we can find some unused headers */
void
Parrot_do_dod_run(struct Parrot_Interp *interpreter)
{
+#ifdef GC_IS_MALLOC
+ struct Small_Object_Pool *header_pool;
+ int j;
+
+#endif /* GC_IS_MALLOC */
if (interpreter->DOD_block_level) {
return;
}
@@ -452,11 +554,29 @@ Parrot_do_dod_run(struct Parrot_Interp *interpreter)
free_unused_PMCs(interpreter);
/* And unused buffers on the free list */
+#ifndef GC_IS_MALLOC
free_unused_buffers(interpreter,
interpreter->arena_base->string_header_pool);
free_unused_buffers(interpreter,
interpreter->arena_base->buffer_header_pool);
+#else /* GC_IS_MALLOC */
+ for (j = -2; j < (INTVAL) interpreter->arena_base->num_sized; j++) {
+ if (j == -2)
+ header_pool = interpreter->arena_base->string_header_pool;
+ else if (j == -1)
+ header_pool = interpreter->arena_base->buffer_header_pool;
+ else
+ header_pool = interpreter->arena_base->sized_header_pools[j];
+ if (header_pool && j < 0) {
+ free_unused_buffers(interpreter, header_pool);
+ }
+ }
+ /* update mem stats */
+#if 0
+ interpreter->memory_allocated = mallinfo().uordblks;
+#endif
+#endif /* GC_IS_MALLOC */
/* Note it */
interpreter->dod_runs++;
View
20 headers.c
@@ -20,9 +20,15 @@
# define BUFFER_HEADERS_PER_ALLOC 1
# define STRING_HEADERS_PER_ALLOC 1
#else
+#ifndef GC_IS_MALLOC
# define PMC_HEADERS_PER_ALLOC 256
# define BUFFER_HEADERS_PER_ALLOC 256
# define STRING_HEADERS_PER_ALLOC 256
+#else /* GC_IS_MALLOC */
+# define PMC_HEADERS_PER_ALLOC 512
+# define BUFFER_HEADERS_PER_ALLOC 512
+# define STRING_HEADERS_PER_ALLOC 512
+#endif /* GC_IS_MALLOC */
#endif
/** PMC Header Functions for small-object lookup table **/
@@ -33,6 +39,9 @@ add_free_pmc(struct Parrot_Interp *interpreter,
{
((PMC *)pmc)->flags = PMC_on_free_list_FLAG;
/* Don't let it point to garbage memory */
+#ifdef GC_IS_MALLOC
+ /* XXX custom destroy ?! */
+#endif /* GC_IS_MALLOC */
((PMC *)pmc)->data = NULL;
/* Copied from add_free_object */
@@ -75,9 +84,20 @@ void
add_free_buffer(struct Parrot_Interp *interpreter,
struct Small_Object_Pool *pool, void *buffer)
{
+#ifdef GC_IS_MALLOC
+ /* free allocated space at bufstart, but not if it is used
+ * COW or it is external
+ */
+ if (((Buffer *)buffer)->bufstart &&
+ !(((Buffer *)buffer)->flags &
+ (BUFFER_COW_FLAG|BUFFER_external_FLAG))) {
+ free(((Buffer *)buffer)->bufstart);
+ }
+#endif /* GC_IS_MALLOC */
((Buffer *)buffer)->flags = BUFFER_on_free_list_FLAG;
/* Use the right length */
((Buffer *)buffer)->buflen = 0;
+ ((Buffer *)buffer)->bufstart = 0;
/* Copied from add_free_object */
*(void **)buffer = pool->free_list;
View
1 lib/Parrot/Configure/RunSteps.pm
@@ -26,6 +26,7 @@ use vars qw(@steps);
auto/jit.pl
auto/funcptr.pl
auto/cgoto.pl
+ auto/gc.pl
gen/config_h.pl
gen/config_pm.pl
gen/makefiles.pl
View
215 malloc-trace.c
@@ -0,0 +1,215 @@
+/* malloc-trace.c
+ *
+ * by Wolfram Gloger 1995.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/time.h>
+#ifdef __linux__
+#include <signal.h>
+#include <asm/signal.h>
+#endif
+
+#include "malloc-trace.h"
+
+#define ACTION_BUF_SIZE 1024
+#define TIMESTAMP_FREQ 50
+#define LOG_NAME "/tmp/mtrace"
+
+static void malloc_trace_destructor(void);
+
+static ACTION buffer[ACTION_BUF_SIZE+1];
+static int buffer_i = 0;
+static int fd = -1;
+static int tracing = 1;
+static int pid = 0;
+static char *initial_brk = 0;
+
+static void
+open_log_file()
+{
+ char name[128];
+ const unsigned version = MTRACE_VERSION;
+#ifdef __linux__
+ char cmdline[128], *s;
+ int count, fd2;
+#endif
+
+ tracing = 0;
+ pid = getpid();
+#ifdef __linux__
+ sprintf(name, "/proc/%d/cmdline", pid);
+ fd2 = open(name, O_RDONLY);
+ if(fd2>=0 && (count = read(fd2, cmdline, 127)) > 0) {
+ close(fd2);
+ cmdline[count] = 0;
+ for(s=cmdline; *s++;); s--;
+ while(--s>cmdline && *s!='/');
+ if(*s == '/') s++;
+ sprintf(name, LOG_NAME ".%.12s.%d", s, pid);
+ } else {
+ sprintf(name, LOG_NAME ".%d", pid);
+ }
+#else
+ sprintf(name, LOG_NAME ".%d", pid);
+#endif
+ fd = open(name, O_WRONLY|O_CREAT, 0600);
+ if(fd >= 0) {
+ write(fd, &version, sizeof(version));
+ write(fd, &initial_brk, sizeof(initial_brk));
+ }
+ tracing = 1;
+}
+
+static void
+malloc_trace_destructor(void)
+{
+ struct timeval t;
+
+ /* produce final timestamp */
+ gettimeofday(&t, NULL);
+ buffer[buffer_i].code = CODE_TIMESTAMP;
+ buffer[buffer_i].size = t.tv_sec;
+ buffer[buffer_i].ptr = (void *)t.tv_usec;
+ buffer[buffer_i].ptr2 = NULL;
+ buffer_i++;
+ if(fd < 0) open_log_file();
+ if(getpid() != pid) { /* Oops, must have forked... */
+ if(fd >= 0) close(fd);
+ return;
+ }
+ if(fd >= 0) write(fd, buffer, buffer_i*sizeof(ACTION));
+ write(2, "<end malloc trace>\n", 19);
+ if(fd >= 0) close(fd);
+}
+
+#ifdef __linux__
+
+static void
+malloc_segv_handler(int i, struct sigcontext_struct sc)
+{
+ tracing = 0;
+ fprintf(stderr, "malloc-trace: caught SEGV signal.\n");
+ fprintf(stderr, "sc.cr2 = %8lx\n", (unsigned long)sc.cr2);
+ malloc_trace_destructor();
+}
+
+#endif
+
+static void
+malloc_record(int code, size_t size, void *ptr, void *ptr2)
+{
+ static long count = 0;
+ struct timeval t;
+
+ if(!tracing) return;
+#ifdef __linux__
+ if(count == 0) signal(SIGSEGV, (void (*)(int))malloc_segv_handler);
+#endif
+ if((count++ % TIMESTAMP_FREQ) == 0) {
+ gettimeofday(&t, NULL);
+ buffer[buffer_i].code = CODE_TIMESTAMP;
+ buffer[buffer_i].size = t.tv_sec;
+ buffer[buffer_i].ptr = (void *)t.tv_usec;
+ buffer[buffer_i].ptr2 = NULL;
+ buffer_i++;
+ }
+ buffer[buffer_i].code = code;
+ buffer[buffer_i].size = size;
+ buffer[buffer_i].ptr = ptr;
+ buffer[buffer_i].ptr2 = ptr2;
+ if(++buffer_i >= ACTION_BUF_SIZE) {
+ if(fd < 0) open_log_file();
+ if(getpid() != pid) { /* Oops, must have forked... */
+ tracing = 0;
+ return;
+ }
+ if(fd >= 0) write(fd, buffer, buffer_i*sizeof(ACTION));
+ buffer_i = 0;
+ }
+}
+
+void* _real_malloc(size_t bytes);
+void* malloc(size_t bytes)
+{
+ void *ptr;
+
+ if(initial_brk == 0) { /* Must be the first time. */
+ initial_brk = sbrk(0);
+ atexit(malloc_trace_destructor);
+ }
+ ptr = _real_malloc(bytes);
+ malloc_record(CODE_MALLOC, bytes, ptr, 0);
+ return ptr;
+}
+#define malloc _real_malloc
+
+void _real_free(void* mem);
+void free(void* mem)
+{
+ malloc_record(CODE_FREE, 0, mem, 0);
+ _real_free(mem);
+}
+#define free _real_free
+
+void* _real_realloc(void* mem, size_t bytes);
+void* realloc(void* mem, size_t bytes)
+{
+ void *ptr;
+
+ ptr = _real_realloc(mem, bytes);
+ malloc_record(CODE_REALLOC, bytes, mem, ptr);
+ return ptr;
+}
+#define realloc _real_realloc
+
+void* _real_memalign(size_t alignment, size_t bytes);
+void* memalign(size_t alignment, size_t bytes)
+{
+ void *ptr;
+
+ if(initial_brk == 0) { /* Must be the first time. */
+ initial_brk = sbrk(0);
+ atexit(malloc_trace_destructor);
+ }
+ ptr = _real_memalign(alignment, bytes);
+ malloc_record(CODE_MEMALIGN, bytes, ptr, (void*)alignment);
+ return ptr;
+}
+#define memalign _real_memalign
+
+void* _real_calloc(size_t n, size_t elem_size);
+void* calloc(size_t n, size_t elem_size)
+{
+ void *ptr;
+
+ if(initial_brk == 0) { /* Must be the first time. */
+ initial_brk = sbrk(0);
+ atexit(malloc_trace_destructor);
+ }
+ ptr = _real_calloc(n, elem_size);
+ malloc_record(CODE_CALLOC, n*elem_size, ptr, 0);
+ return ptr;
+}
+#define calloc _real_calloc
+
+void _real_cfree(void *mem);
+void cfree(void *mem)
+{
+ malloc_record(CODE_CFREE, 0, mem, 0);
+ _real_cfree(mem);
+}
+#define cfree _real_cfree
+
+#include "malloc.c"
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * compile-command: "gcc -Wall -O -fpic -shared -o malloc-trace.so malloc-trace.c"
+ * End:
+ */
View
5,567 malloc.c
5,567 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
85 res_lea.c
@@ -0,0 +1,85 @@
+/* resources */
+#include <assert.h>
+#include "parrot/parrot.h"
+void
+Parrot_go_collect(struct Parrot_Interp *interpreter)
+{
+ if (interpreter->GC_block_level) {
+ return;
+ }
+ interpreter->collect_runs++; /* fake it */
+}
+void *
+Parrot_reallocate(struct Parrot_Interp *interpreter, void *from, size_t size)
+{
+ Buffer * buffer = from;
+ void *p;
+ size_t oldlen = buffer->buflen;
+ p = realloc(buffer->bufstart, size);
+ if (size > buffer->buflen)
+ memset((char*)p + oldlen, 0, size - oldlen);
+ buffer->buflen = size;
+ buffer->bufstart = p;
+ return p;
+}
+
+void *
+Parrot_allocate(struct Parrot_Interp *interpreter, void *buffer, size_t size)
+{
+ Buffer * b = buffer;
+ b->bufstart = calloc(1, size);
+ b->buflen = size;
+ return b;
+}
+
+void *
+Parrot_reallocate_string(struct Parrot_Interp *interpreter, STRING *str,
+ size_t size)
+{
+ void *p;
+ size_t pad, rsize;
+ pad = STRING_ALIGNMENT - 1;
+ /* 2 chars string tail, first seems to be clobbered */
+ size = ((size + pad + 2) & ~pad) - 2;
+ p = realloc(str->bufstart, size + 2);
+ str->strstart = str->bufstart = p;
+ ((char*)str->bufstart)[size+1] = 0;
+ str->buflen = size;
+ return p;
+}
+
+void *
+Parrot_allocate_string(struct Parrot_Interp *interpreter, STRING *str,
+ size_t size)
+{
+ void *p = 0;
+ size_t pad;
+#if 0
+ if (size)
+#endif
+ {
+#if 0
+ pad = STRING_ALIGNMENT - 1;
+ size = ((size + pad + 2) & ~pad) - 2;
+#endif
+ p = calloc(1, size + 2);
+ }
+ str->strstart = str->bufstart = p;
+ str->buflen = size;
+ return str;
+}
+
+void
+Parrot_initialize_memory_pools(struct Parrot_Interp *interpreter)
+{
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+*/
View
120 src/dod.c
@@ -308,6 +308,57 @@ free_unused_PMCs(struct Parrot_Interp *interpreter)
interpreter->arena_base->pmc_pool->total_objects - total_used;
}
+#ifdef GC_IS_MALLOC
+
+/* find other users of COW's bufstart */
+static void
+used_cow(struct Parrot_Interp *interpreter, struct Small_Object_Pool *pool)
+{
+ UINTVAL object_size = pool->object_size;
+ struct Small_Object_Arena *cur_arena;
+ UINTVAL i;
+ Buffer *b;
+ char *tail;
+
+#ifdef LEA_DEBUG
+ /* check/clear tail, e.g. on changes in string.c or res.c */
+ for (cur_arena = pool->last_Arena;
+ NULL != cur_arena;
+ cur_arena = cur_arena->prev) {
+ b = cur_arena->start_objects;
+ for (i = 0; i < cur_arena->used; i++) {
+ if ((b->flags & BUFFER_COW_FLAG) && b->bufstart &&
+ !(b->flags & BUFFER_external_FLAG)) {
+ tail = (char*)b->bufstart + b->buflen + 1;
+ assert(*tail == 0);
+ *tail = 0;
+ }
+ b = (Buffer *)((char *)b + object_size);
+ }
+ }
+#endif
+
+ for (cur_arena = pool->last_Arena;
+ NULL != cur_arena;
+ cur_arena = cur_arena->prev) {
+ b = cur_arena->start_objects;
+ for (i = 0; i < cur_arena->used; i++) {
+ if ((b->flags & BUFFER_COW_FLAG) &&
+ !(b->flags & BUFFER_external_FLAG)) {
+ tail = (char*)b->bufstart + b->buflen + 1;
+ /* mark living and dead users of this bufstart
+ * tail is cleared in *allocate_string */
+ if (b->flags & BUFFER_live_FLAG)
+ *tail |= 0x2;
+ else
+ *tail |= 0x1;
+ }
+ b = (Buffer *)((char *)b + object_size);
+ }
+ }
+}
+
+#endif /* GC_IS_MALLOC */
/* Put any buffers that are now unused, on to the free list
* Avoid buffers that are immune from collection (ie, constant) */
static void
@@ -317,13 +368,26 @@ free_unused_buffers(struct Parrot_Interp *interpreter,
struct Small_Object_Arena *cur_arena;
UINTVAL i, total_used = 0;
UINTVAL object_size = pool->object_size;
+#ifdef GC_IS_MALLOC
+ char *tail;
+#endif /* GC_IS_MALLOC */
+#ifdef GC_IS_MALLOC
+ used_cow(interpreter, pool);
+#endif /* GC_IS_MALLOC */
/* Run through all the buffer header pools and mark */
for (cur_arena = pool->last_Arena;
NULL != cur_arena;
cur_arena = cur_arena->prev) {
Buffer *b = cur_arena->start_objects;
for (i = 0; i < cur_arena->used; i++) {
+#ifdef GC_IS_MALLOC
+ if ((b->flags & BUFFER_COW_FLAG) &&
+ !(b->flags & BUFFER_external_FLAG))
+ tail = (char*)b->bufstart + b->buflen + 1;
+ else
+ tail = 0;
+#endif /* GC_IS_MALLOC */
/* If it's not live or on the free list, put it on the free list.
* Note that it is technically possible to have a Buffer be both
* on_free_list and live, because of our conservative stack-walk
@@ -332,6 +396,7 @@ free_unused_buffers(struct Parrot_Interp *interpreter,
| BUFFER_constant_FLAG
| BUFFER_live_FLAG )))
{
+#ifndef GC_IS_MALLOC
if (pool->mem_pool) {
if (!(b->flags & BUFFER_COW_FLAG)) {
((struct Memory_Pool *)
@@ -341,10 +406,27 @@ free_unused_buffers(struct Parrot_Interp *interpreter,
((struct Memory_Pool *)
pool->mem_pool)->possibly_reclaimable += b->buflen;
}
+#else /* GC_IS_MALLOC */
+ /* if only dead users, this one may be freed */
+ if (tail && *tail == 0x1)
+ b->flags &= ~BUFFER_COW_FLAG;
+ /* don't free this bufstart this time, because
+ * tail will be invalid then -
+ * if we want to free immediately, we need extended
+ * bookkeeping to free exactly the last user
+ */
+ else
+#endif /* GC_IS_MALLOC */
add_free_buffer(interpreter, pool, b);
} else if (!(b->flags & BUFFER_on_free_list_FLAG)) {
total_used++;
}
+#ifdef GC_IS_MALLOC
+ /* clear tail for next dod run and
+ * don't unset COW on other possbily dead users */
+ if (tail)
+ *tail = 0;
+#endif /* GC_IS_MALLOC */
b->flags &= ~BUFFER_live_FLAG;
b = (Buffer *)((char *)b + object_size);
}
@@ -429,11 +511,31 @@ trace_system_stack(struct Parrot_Interp *interpreter, PMC *last)
}
#endif
+#ifdef GC_IS_MALLOC
+struct mallinfo {
+ int arena; /* non-mmapped space allocated from system */
+ int ordblks; /* number of free chunks */
+ int smblks; /* number of fastbin blocks */
+ int hblks; /* number of mmapped regions */
+ int hblkhd; /* space in mmapped regions */
+ int usmblks; /* maximum total allocated space */
+ int fsmblks; /* space available in freed fastbin blocks */
+ int uordblks; /* total allocated space */
+ int fordblks; /* total free space */
+ int keepcost; /* top-most, releasable (via malloc_trim) space */
+};
+extern struct mallinfo mallinfo(void);
+#endif /* GC_IS_MALLOC */
/* See if we can find some unused headers */
void
Parrot_do_dod_run(struct Parrot_Interp *interpreter)
{
+#ifdef GC_IS_MALLOC
+ struct Small_Object_Pool *header_pool;
+ int j;
+
+#endif /* GC_IS_MALLOC */
if (interpreter->DOD_block_level) {
return;
}
@@ -452,11 +554,29 @@ Parrot_do_dod_run(struct Parrot_Interp *interpreter)
free_unused_PMCs(interpreter);
/* And unused buffers on the free list */
+#ifndef GC_IS_MALLOC
free_unused_buffers(interpreter,
interpreter->arena_base->string_header_pool);
free_unused_buffers(interpreter,
interpreter->arena_base->buffer_header_pool);
+#else /* GC_IS_MALLOC */
+ for (j = -2; j < (INTVAL) interpreter->arena_base->num_sized; j++) {
+ if (j == -2)
+ header_pool = interpreter->arena_base->string_header_pool;
+ else if (j == -1)
+ header_pool = interpreter->arena_base->buffer_header_pool;
+ else
+ header_pool = interpreter->arena_base->sized_header_pools[j];
+ if (header_pool && j < 0) {
+ free_unused_buffers(interpreter, header_pool);
+ }
+ }
+ /* update mem stats */
+#if 0
+ interpreter->memory_allocated = mallinfo().uordblks;
+#endif
+#endif /* GC_IS_MALLOC */
/* Note it */
interpreter->dod_runs++;
View
20 src/headers.c
@@ -20,9 +20,15 @@
# define BUFFER_HEADERS_PER_ALLOC 1
# define STRING_HEADERS_PER_ALLOC 1
#else
+#ifndef GC_IS_MALLOC
# define PMC_HEADERS_PER_ALLOC 256
# define BUFFER_HEADERS_PER_ALLOC 256
# define STRING_HEADERS_PER_ALLOC 256
+#else /* GC_IS_MALLOC */
+# define PMC_HEADERS_PER_ALLOC 512
+# define BUFFER_HEADERS_PER_ALLOC 512
+# define STRING_HEADERS_PER_ALLOC 512
+#endif /* GC_IS_MALLOC */
#endif
/** PMC Header Functions for small-object lookup table **/
@@ -33,6 +39,9 @@ add_free_pmc(struct Parrot_Interp *interpreter,
{
((PMC *)pmc)->flags = PMC_on_free_list_FLAG;
/* Don't let it point to garbage memory */
+#ifdef GC_IS_MALLOC
+ /* XXX custom destroy ?! */
+#endif /* GC_IS_MALLOC */
((PMC *)pmc)->data = NULL;
/* Copied from add_free_object */
@@ -75,9 +84,20 @@ void
add_free_buffer(struct Parrot_Interp *interpreter,
struct Small_Object_Pool *pool, void *buffer)
{
+#ifdef GC_IS_MALLOC
+ /* free allocated space at bufstart, but not if it is used
+ * COW or it is external
+ */
+ if (((Buffer *)buffer)->bufstart &&
+ !(((Buffer *)buffer)->flags &
+ (BUFFER_COW_FLAG|BUFFER_external_FLAG))) {
+ free(((Buffer *)buffer)->bufstart);
+ }
+#endif /* GC_IS_MALLOC */
((Buffer *)buffer)->flags = BUFFER_on_free_list_FLAG;
/* Use the right length */
((Buffer *)buffer)->buflen = 0;
+ ((Buffer *)buffer)->bufstart = 0;
/* Copied from add_free_object */
*(void **)buffer = pool->free_list;
View
215 src/malloc-trace.c
@@ -0,0 +1,215 @@
+/* malloc-trace.c
+ *
+ * by Wolfram Gloger 1995.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/time.h>
+#ifdef __linux__
+#include <signal.h>
+#include <asm/signal.h>
+#endif
+
+#include "malloc-trace.h"
+
+#define ACTION_BUF_SIZE 1024
+#define TIMESTAMP_FREQ 50
+#define LOG_NAME "/tmp/mtrace"
+
+static void malloc_trace_destructor(void);
+
+static ACTION buffer[ACTION_BUF_SIZE+1];
+static int buffer_i = 0;
+static int fd = -1;
+static int tracing = 1;
+static int pid = 0;
+static char *initial_brk = 0;
+
+static void
+open_log_file()
+{
+ char name[128];
+ const unsigned version = MTRACE_VERSION;
+#ifdef __linux__
+ char cmdline[128], *s;
+ int count, fd2;
+#endif
+
+ tracing = 0;
+ pid = getpid();
+#ifdef __linux__
+ sprintf(name, "/proc/%d/cmdline", pid);
+ fd2 = open(name, O_RDONLY);
+ if(fd2>=0 && (count = read(fd2, cmdline, 127)) > 0) {
+ close(fd2);
+ cmdline[count] = 0;
+ for(s=cmdline; *s++;); s--;
+ while(--s>cmdline && *s!='/');
+ if(*s == '/') s++;
+ sprintf(name, LOG_NAME ".%.12s.%d", s, pid);
+ } else {
+ sprintf(name, LOG_NAME ".%d", pid);
+ }
+#else
+ sprintf(name, LOG_NAME ".%d", pid);
+#endif
+ fd = open(name, O_WRONLY|O_CREAT, 0600);
+ if(fd >= 0) {
+ write(fd, &version, sizeof(version));
+ write(fd, &initial_brk, sizeof(initial_brk));
+ }
+ tracing = 1;
+}
+
+static void
+malloc_trace_destructor(void)
+{
+ struct timeval t;
+
+ /* produce final timestamp */
+ gettimeofday(&t, NULL);
+ buffer[buffer_i].code = CODE_TIMESTAMP;
+ buffer[buffer_i].size = t.tv_sec;
+ buffer[buffer_i].ptr = (void *)t.tv_usec;
+ buffer[buffer_i].ptr2 = NULL;
+ buffer_i++;
+ if(fd < 0) open_log_file();
+ if(getpid() != pid) { /* Oops, must have forked... */
+ if(fd >= 0) close(fd);
+ return;
+ }
+ if(fd >= 0) write(fd, buffer, buffer_i*sizeof(ACTION));
+ write(2, "<end malloc trace>\n", 19);
+ if(fd >= 0) close(fd);
+}
+
+#ifdef __linux__
+
+static void
+malloc_segv_handler(int i, struct sigcontext_struct sc)
+{
+ tracing = 0;
+ fprintf(stderr, "malloc-trace: caught SEGV signal.\n");
+ fprintf(stderr, "sc.cr2 = %8lx\n", (unsigned long)sc.cr2);
+ malloc_trace_destructor();
+}
+
+#endif
+
+static void
+malloc_record(int code, size_t size, void *ptr, void *ptr2)
+{
+ static long count = 0;
+ struct timeval t;
+
+ if(!tracing) return;
+#ifdef __linux__
+ if(count == 0) signal(SIGSEGV, (void (*)(int))malloc_segv_handler);
+#endif
+ if((count++ % TIMESTAMP_FREQ) == 0) {
+ gettimeofday(&t, NULL);
+ buffer[buffer_i].code = CODE_TIMESTAMP;
+ buffer[buffer_i].size = t.tv_sec;
+ buffer[buffer_i].ptr = (void *)t.tv_usec;
+ buffer[buffer_i].ptr2 = NULL;
+ buffer_i++;
+ }
+ buffer[buffer_i].code = code;
+ buffer[buffer_i].size = size;
+ buffer[buffer_i].ptr = ptr;
+ buffer[buffer_i].ptr2 = ptr2;
+ if(++buffer_i >= ACTION_BUF_SIZE) {
+ if(fd < 0) open_log_file();
+ if(getpid() != pid) { /* Oops, must have forked... */
+ tracing = 0;
+ return;
+ }
+ if(fd >= 0) write(fd, buffer, buffer_i*sizeof(ACTION));
+ buffer_i = 0;
+ }
+}
+
+void* _real_malloc(size_t bytes);
+void* malloc(size_t bytes)
+{
+ void *ptr;
+
+ if(initial_brk == 0) { /* Must be the first time. */
+ initial_brk = sbrk(0);
+ atexit(malloc_trace_destructor);
+ }
+ ptr = _real_malloc(bytes);
+ malloc_record(CODE_MALLOC, bytes, ptr, 0);
+ return ptr;
+}
+#define malloc _real_malloc
+
+void _real_free(void* mem);
+void free(void* mem)
+{
+ malloc_record(CODE_FREE, 0, mem, 0);
+ _real_free(mem);
+}
+#define free _real_free
+
+void* _real_realloc(void* mem, size_t bytes);
+void* realloc(void* mem, size_t bytes)
+{
+ void *ptr;
+
+ ptr = _real_realloc(mem, bytes);
+ malloc_record(CODE_REALLOC, bytes, mem, ptr);
+ return ptr;
+}
+#define realloc _real_realloc
+
+void* _real_memalign(size_t alignment, size_t bytes);
+void* memalign(size_t alignment, size_t bytes)
+{
+ void *ptr;
+
+ if(initial_brk == 0) { /* Must be the first time. */
+ initial_brk = sbrk(0);
+ atexit(malloc_trace_destructor);
+ }
+ ptr = _real_memalign(alignment, bytes);
+ malloc_record(CODE_MEMALIGN, bytes, ptr, (void*)alignment);
+ return ptr;
+}
+#define memalign _real_memalign
+
+void* _real_calloc(size_t n, size_t elem_size);
+void* calloc(size_t n, size_t elem_size)
+{
+ void *ptr;
+
+ if(initial_brk == 0) { /* Must be the first time. */
+ initial_brk = sbrk(0);
+ atexit(malloc_trace_destructor);
+ }
+ ptr = _real_calloc(n, elem_size);
+ malloc_record(CODE_CALLOC, n*elem_size, ptr, 0);
+ return ptr;
+}
+#define calloc _real_calloc
+
+void _real_cfree(void *mem);
+void cfree(void *mem)
+{
+ malloc_record(CODE_CFREE, 0, mem, 0);
+ _real_cfree(mem);
+}
+#define cfree _real_cfree
+
+#include "malloc.c"
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * compile-command: "gcc -Wall -O -fpic -shared -o malloc-trace.so malloc-trace.c"
+ * End:
+ */
View
5,567 src/malloc.c
5,567 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
85 src/res_lea.c
@@ -0,0 +1,85 @@
+/* resources */
+#include <assert.h>
+#include "parrot/parrot.h"
+void
+Parrot_go_collect(struct Parrot_Interp *interpreter)
+{
+ if (interpreter->GC_block_level) {
+ return;
+ }
+ interpreter->collect_runs++; /* fake it */
+}
+void *
+Parrot_reallocate(struct Parrot_Interp *interpreter, void *from, size_t size)
+{
+ Buffer * buffer = from;
+ void *p;
+ size_t oldlen = buffer->buflen;
+ p = realloc(buffer->bufstart, size);
+ if (size > buffer->buflen)
+ memset((char*)p + oldlen, 0, size - oldlen);
+ buffer->buflen = size;
+ buffer->bufstart = p;
+ return p;
+}
+
+void *
+Parrot_allocate(struct Parrot_Interp *interpreter, void *buffer, size_t size)
+{
+ Buffer * b = buffer;
+ b->bufstart = calloc(1, size);
+ b->buflen = size;
+ return b;
+}
+
+void *
+Parrot_reallocate_string(struct Parrot_Interp *interpreter, STRING *str,
+ size_t size)
+{
+ void *p;
+ size_t pad, rsize;
+ pad = STRING_ALIGNMENT - 1;
+ /* 2 chars string tail, first seems to be clobbered */
+ size = ((size + pad + 2) & ~pad) - 2;
+ p = realloc(str->bufstart, size + 2);
+ str->strstart = str->bufstart = p;
+ ((char*)str->bufstart)[size+1] = 0;
+ str->buflen = size;
+ return p;
+}
+
+void *
+Parrot_allocate_string(struct Parrot_Interp *interpreter, STRING *str,
+ size_t size)
+{
+ void *p = 0;
+ size_t pad;
+#if 0
+ if (size)
+#endif
+ {
+#if 0
+ pad = STRING_ALIGNMENT - 1;
+ size = ((size + pad + 2) & ~pad) - 2;
+#endif
+ p = calloc(1, size + 2);
+ }
+ str->strstart = str->bufstart = p;
+ str->buflen = size;
+ return str;
+}
+
+void
+Parrot_initialize_memory_pools(struct Parrot_Interp *interpreter)
+{
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+*/
View
47 src/string.c
@@ -18,32 +18,31 @@ static const CHARTYPE *string_unicode_type;
#define EXTRA_SIZE 4
/* String COW support */
+
+/* make a copy of string's data:
+ * copy used string data from strstart to a newly
+ * allocated string
+ * the header stays the same
+ */
static void
unmake_COW(struct Parrot_Interp *interpreter, STRING *s)
{
-#if 0
- if (s->flags & BUFFER_constant_FLAG) {
- /* this happens when we call string_to_cstring on
- * a constant string in order to print it
- */
- internal_exception(INVALID_OPERATION,
- "Cannot unmake COW on a constant header");
- }
- else
-#endif
if (s->flags & (BUFFER_COW_FLAG|BUFFER_constant_FLAG)) {
+ void *p;
+ UINTVAL size;
interpreter->GC_block_level++;
interpreter->DOD_block_level++;
/* Make the copy point to only the portion of the string that
* we are actually using. */
- s->bufstart = s->strstart;
- s->buflen = s->bufused;
-
+ p = s->strstart;
+ size = s->bufused;
/* Create new pool data for this header to use,
* independant of the original COW data */
- Parrot_reallocate_string(interpreter, s, s->buflen);
- s->flags &= ~(UINTVAL)(BUFFER_COW_FLAG | BUFFER_constant_FLAG);
+ s->flags &= ~BUFFER_constant_FLAG;
+ Parrot_allocate_string(interpreter, s, size);
+ mem_sys_memcopy(s->bufstart, p, size);
+ s->flags &= ~(UINTVAL)(BUFFER_COW_FLAG | BUFFER_external_FLAG);
interpreter->GC_block_level--;
interpreter->DOD_block_level--;
}
@@ -399,8 +398,7 @@ string_transcode(struct Parrot_Interp *interpreter,
INTVAL
string_compute_strlen(STRING *s)
{
- s->strlen = s->encoding->characters(s->bufstart, s->bufused) -
- ((UINTVAL)s->strstart - (UINTVAL)s->bufstart);
+ s->strlen = s->encoding->characters(s->strstart, s->bufused);
return s->strlen;
}
@@ -972,25 +970,16 @@ string_from_num(struct Parrot_Interp * interpreter, FLOATVAL f)
const char *
string_to_cstring(struct Parrot_Interp * interpreter, STRING * s)
{
- char *cstring;
-
- /* We shouldn't modify a constant string,
- * so instead create a new copy of it */
- if (s->flags & BUFFER_constant_FLAG) {
- s = make_COW_reference(interpreter,s);
- }
unmake_COW(interpreter, s);
if (s->buflen == s->bufused) {
string_grow(interpreter, s, 1);
}
- cstring = s->strstart;
-
- cstring[s->bufused] = 0;
-
- return cstring;
+ ((char *)s->strstart)[s->bufused] = 0;
+ /* don't return local vars, return the right thing */
+ return (char*)s->strstart;
}
View
47 string.c
@@ -18,32 +18,31 @@ static const CHARTYPE *string_unicode_type;
#define EXTRA_SIZE 4
/* String COW support */
+
+/* make a copy of string's data:
+ * copy used string data from strstart to a newly
+ * allocated string
+ * the header stays the same
+ */
static void
unmake_COW(struct Parrot_Interp *interpreter, STRING *s)
{
-#if 0
- if (s->flags & BUFFER_constant_FLAG) {
- /* this happens when we call string_to_cstring on
- * a constant string in order to print it
- */
- internal_exception(INVALID_OPERATION,
- "Cannot unmake COW on a constant header");
- }
- else
-#endif
if (s->flags & (BUFFER_COW_FLAG|BUFFER_constant_FLAG)) {
+ void *p;
+ UINTVAL size;
interpreter->GC_block_level++;
interpreter->DOD_block_level++;
/* Make the copy point to only the portion of the string that
* we are actually using. */
- s->bufstart = s->strstart;
- s->buflen = s->bufused;
-
+ p = s->strstart;
+ size = s->bufused;
/* Create new pool data for this header to use,
* independant of the original COW data */
- Parrot_reallocate_string(interpreter, s, s->buflen);
- s->flags &= ~(UINTVAL)(BUFFER_COW_FLAG | BUFFER_constant_FLAG);
+ s->flags &= ~BUFFER_constant_FLAG;
+ Parrot_allocate_string(interpreter, s, size);
+ mem_sys_memcopy(s->bufstart, p, size);
+ s->flags &= ~(UINTVAL)(BUFFER_COW_FLAG | BUFFER_external_FLAG);
interpreter->GC_block_level--;
interpreter->DOD_block_level--;
}
@@ -399,8 +398,7 @@ string_transcode(struct Parrot_Interp *interpreter,
INTVAL
string_compute_strlen(STRING *s)
{
- s->strlen = s->encoding->characters(s->bufstart, s->bufused) -
- ((UINTVAL)s->strstart - (UINTVAL)s->bufstart);
+ s->strlen = s->encoding->characters(s->strstart, s->bufused);
return s->strlen;
}
@@ -972,25 +970,16 @@ string_from_num(struct Parrot_Interp * interpreter, FLOATVAL f)
const char *
string_to_cstring(struct Parrot_Interp * interpreter, STRING * s)
{
- char *cstring;
-
- /* We shouldn't modify a constant string,
- * so instead create a new copy of it */
- if (s->flags & BUFFER_constant_FLAG) {
- s = make_COW_reference(interpreter,s);
- }
unmake_COW(interpreter, s);
if (s->buflen == s->bufused) {
string_grow(interpreter, s, 1);
}
- cstring = s->strstart;
-
- cstring[s->bufused] = 0;
-
- return cstring;
+ ((char *)s->strstart)[s->bufused] = 0;
+ /* don't return local vars, return the right thing */
+ return (char*)s->strstart;
}

0 comments on commit c0142c7

Please sign in to comment.