Commit f54d4287 by Bryce McKinlay

Initial revision

From-SVN: r42370
parent 9d2f2c45
While the GC should work on MacOS X Server, MacOS X and Darwin, I only tested
it on MacOS X Server.
I've added a PPC assembly version of GC_push_regs(), thus the setjmp() hack is
no longer necessary. Incremental collection is supported via mprotect/signal.
The current solution isn't really optimal because the signal handler must decode
the faulting PPC machine instruction in order to find the correct heap address.
Further, it must poke around in the register state which the kernel saved away
in some obscure register state structure before it calls the signal handler -
needless to say the layout of this structure is no where documented.
Threads and dynamic libraries are not yet supported (adding dynamic library
support via the low-level dyld API shouldn't be that hard).
The original MacOS X port was brought to you by Andrew Stone.
June, 1 2000
Dietmar Planitzer
dave.pl@ping.at
The code assumes static linking, and a single thread. The editor de has
not been ported. The cord test program has. The supplied OS2_MAKEFILE
assumes the IBM C Set/2 environment, but the code shouldn't.
Since we haven't figured out hoe to do perform partial links or to build static
libraries, clients currently need to link against a long list of executables.
This is an attempt to acknowledge early contributions to the garbage
collector. Later contributions should instead be mentioned in
README.changes.
HISTORY -
Early versions of this collector were developed as a part of research
projects supported in part by the National Science Foundation
and the Defense Advance Research Projects Agency.
The garbage collector originated as part of the run-time system for
the Russell programming language implementation. The first version of the
garbage collector was written primarily by Al Demers. It was then refined
and mostly rewritten, primarily by Hans-J. Boehm, at Cornell U.,
the University of Washington, Rice University (where it was first used for
C and assembly code), Xerox PARC, SGI, and HP Labs. However, significant
contributions have also been made by many others.
Some other contributors:
More recent contributors are mentioned in the modification history in
README.changes. My apologies for any omissions.
The SPARC specific code was originally contributed by Mark Weiser.
The Encore Multimax modifications were supplied by
Kevin Kenny (kenny@m.cs.uiuc.edu). The adaptation to the IBM PC/RT is largely
due to Vernon Lee, on machines made available to Rice by IBM.
Much of the HP specific code and a number of good suggestions for improving the
generic code are due to Walter Underwood.
Robert Brazile (brazile@diamond.bbn.com) originally supplied the ULTRIX code.
Al Dosser (dosser@src.dec.com) and Regis Cridlig (Regis.Cridlig@cl.cam.ac.uk)
subsequently provided updates and information on variation between ULTRIX
systems. Parag Patel (parag@netcom.com) supplied the A/UX code.
Jesper Peterson(jep@mtiame.mtia.oz.au), Michel Schinz, and
Martin Tauchmann (martintauchmann@bigfoot.com) supplied the Amiga port.
Thomas Funke (thf@zelator.in-berlin.de(?)) and
Brian D.Carlstrom (bdc@clark.lcs.mit.edu) supplied the NeXT ports.
Douglas Steel (doug@wg.icl.co.uk) provided ICL DRS6000 code.
Bill Janssen (janssen@parc.xerox.com) supplied the SunOS dynamic loader
specific code. Manuel Serrano (serrano@cornas.inria.fr) supplied linux and
Sony News specific code. Al Dosser provided Alpha/OSF/1 code. He and
Dave Detlefs(detlefs@src.dec.com) also provided several generic bug fixes.
Alistair G. Crooks(agc@uts.amdahl.com) supplied the NetBSD and 386BSD ports.
Jeffrey Hsu (hsu@soda.berkeley.edu) provided the FreeBSD port.
Brent Benson (brent@jade.ssd.csd.harris.com) ported the collector to
a Motorola 88K processor running CX/UX (Harris NightHawk).
Ari Huttunen (Ari.Huttunen@hut.fi) generalized the OS/2 port to
nonIBM development environments (a nontrivial task).
Patrick Beard (beard@cs.ucdavis.edu) provided the initial MacOS port.
David Chase, then at Olivetti Research, suggested several improvements.
Scott Schwartz (schwartz@groucho.cse.psu.edu) supplied some of the
code to save and print call stacks for leak detection on a SPARC.
Jesse Hull and John Ellis supplied the C++ interface code.
Zhong Shao performed much of the experimentation that led to the
current typed allocation facility. (His dynamic type inference code hasn't
made it into the released version of the collector, yet.)
Copyright (c) 1993-1994 by Xerox Corporation. All rights reserved.
THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
Permission is hereby granted to use or copy this program
for any purpose, provided the above notices are retained on all copies.
Permission to modify the code and to distribute modified code is granted,
provided the above notices are retained, and a notice that the code was
modified is included with the above copyright notice.
Please send bug reports to Hans-J. Boehm (Hans_Boehm@hp.com or
boehm@acm.org).
This is a string packages that uses a tree-based representation.
See cord.h for a description of the functions provided. Ec.h describes
"extensible cords", which are essentially output streams that write
to a cord. These allow for efficient construction of cords without
requiring a bound on the size of a cord.
de.c is a very dumb text editor that illustrates the use of cords.
It maintains a list of file versions. Each version is simply a
cord representing the file contents. Nonetheless, standard
editing operations are efficient, even on very large files.
(Its 3 line "user manual" can be obtained by invoking it without
arguments. Note that ^R^N and ^R^P move the cursor by
almost a screen. It does not understand tabs, which will show
up as highlighred "I"s. Use the UNIX "expand" program first.)
To build the editor, type "make cord/de" in the gc directory.
This package assumes an ANSI C compiler such as gcc. It will
not compile with an old-style K&R compiler.
Note that CORD_printf iand friends use C functions with variable numbers
of arguments in non-standard-conforming ways. This code is known to
break on some platforms, notably PowerPC. It should be possible to
build the remainder of the library (everything but cordprnt.c) on
any platform that supports the collector.
Debugging suggestions:
****If you get a segmentation fault or bus error while debugging with a debugger:
If the fault occurred in GC_find_limit, or with incremental collection enabled, this is probably normal. The collector installs handlers to take care of these. You will not see these unless you are using a debugger. Your debugger should allow you to continue. It's preferable to tell the debugger to ignore SIGBUS and SIGSEGV ("handle" in gdb, "ignore" in most versions of dbx) and set a breakpoint in abort. The collector will call abort if the signal had another cause, and there was not other handler previously installed. I recommend debugging without incremental collection if possible. (This applies directly to UNIX systems. Debugging with incremental collection under win32 is worse. See README.win32.)
****If you get warning messages informing you that the collector needed to allocate blacklisted blocks:
0) Ignore these warnings while you are using GC_DEBUG. Some of the routines mentioned below don't have debugging equivalents. (Alternatively, write the missing routines and send them to me.)
1) Replace allocator calls that request large blocks with calls to GC_malloc_ignore_off_page or GC_malloc_atomic_ignore_off_page. You may want to set a breakpoint in GC_default_warn_proc to help you identify such calls. Make sure that a pointer to somewhere near the beginning of the resulting block is maintained in a (preferably volatile) variable as long as the block is needed.
2) If the large blocks are allocated with realloc, I suggest instead allocating them with something like the following. Note that the realloc size increment should be fairly large (e.g. a factor of 3/2) for this to exhibit reasonable performance. But we all know we should do that anyway.
void * big_realloc(void *p, size_t new_size)
{
size_t old_size = GC_size(p);
void * result;
if (new_size <= 10000) return(GC_realloc(p, new_size));
if (new_size <= old_size) return(p);
result = GC_malloc_ignore_off_page(new_size);
if (result == 0) return(0);
memcpy(result,p,old_size);
GC_free(p);
return(result);
}
3) In the unlikely case that even relatively small object (<20KB) allocations are triggering these warnings, then your address space contains lots of "bogus pointers", i.e. values that appear to be pointers but aren't. Usually this can be solved by using GC_malloc_atomic or the routines in gc_typed.h to allocate large pointerfree regions of bitmaps, etc. Sometimes the problem can be solved with trivial changes of encoding in certain values. It is possible, though not pleasant, to identify the source of the bogus pointers by setting a breakpoint in GC_add_to_black_list_stack, and looking at the value of current_p in the GC_mark_from_mark_stack frame. Current_p contains the address of the bogus pointer.
4) If you get only a fixed number of these warnings, you are probably only introducing a bounded leak by ignoring them. If the data structures being allocated are intended to be permanent, then it is also safe to ignore them. The warnings can be turned off by calling GC_set_warn_proc with a procedure that ignores these warnings (e.g. by doing absolutely nothing).
****If the collector dies in GC_malloc while trying to remove a free list element:
1) With > 99% probability, you wrote past the end of an allocated object. Try setting GC_DEBUG and using the debugging facilities in gc.h.
****If the heap grows too much:
1) Consider using GC_malloc_atomic for objects containing nonpointers. This is especially important for large arrays containg compressed data, pseudo-random numbers, and the like. (This isn't all that likely to solve your problem, but it's a useful and easy optimization anyway, and this is a good time to try it.) If you allocate large objects containg only one or two pointers at the beginning, either try the typed allocation primitives is gc.h, or separate out the pointerfree component.
2) If you are using the collector in its default mode, with interior pointer recognition enabled, consider using GC_malloc_ignore_off_page to allocate large objects. (See gc.h and above for details. Large means > 100K in most environments.)
3) GC_print_block_list() will print a list of all currently allocated heap blocks and what size objects they contain. GC_print_hblkfreelist() will print a list of free heap blocks, and whether they are blacklisted. GC_dump calls both of these, and also prints information about heap sections, and root segments.
4) Build the collector with -DKEEP_BACK_PTRS, and use the backptr.h
interface to determine why objects are being retained.
****If the collector appears to be losing objects:
1) Replace all calls to GC_malloc_atomic and typed allocation by GC_malloc calls. If this fixes the problem, gradually reinsert your optimizations.
2) You may also want to try the safe(r) pointer manipulation primitives in gc.h. But those are hard to use until the preprocessor becomes available.
3) Try using the GC_DEBUG facilities. This is less likely to be successful here than if the collector crashes.
[The rest of these are primarily for wizards. You shouldn't need them unless you're doing something really strange, or debugging a collector port.]
4) Don't turn on incremental collection. If that fixes the problem, suspect a bug in the dirty bit implementation. Try compiling with -DCHECKSUMS to check for modified, but supposedly clean, pages.
5) On a SPARC, in a single-threaded environment, GC_print_callers(GC_arrays._last_stack) prints a cryptic stack trace as of the time of the last collection. (You will need a debugger to decipher the result.) The question to ask then is "why should this object have been accessible at the time of the last collection? Where was a pointer to it stored?". This facility should be easy to add for some other collector ports (namely if it's easy to traverse stack frames), but will be hard for others.
6) "print *GC_find_header(p)" in dbx or gdb will print the garbage collector block header information associated with the object p (e.g. object size, etc.)
7) GC_is_marked(p) determines whether p is the base address of a marked object. Note that objects allocated since the last collection should not be marked, and that unmarked objects are reclaimed incrementally. It's usually most interesting to set a breakpoint in GC_finish_collection and then to determine how much of the damaged data structure is marked at that point.
8) Look at the tracing facility in mark.c. (Ignore this suggestion unless you are very familiar with collector internals.)
9) [From Melissa O'Neill:]
If you're using multiple threads, double check that all thread
creation goes through the GC_ wrapper functions rather than
calling the thread-creation functions themselves (e.g.,
GC_pthread_create rather than pthread_create). The gc.h header
file includes suitable preprocessor definitions to accomplish
this mapping transparently -- the question is: are you including
it in all the modules that create threads?
[Original version supplied by Xiaokun Zhu <xiaokun@aero.gla.ac.uk>]
[This version came mostly from Gary Leavens. ]
Look first at Makefile.dj, and possibly change the definitions of
RM and MV if you don't have rm and mv installed.
Then use Makefile.dj to compile the garbage collector.
For example, you can do:
make -f Makefile.dj test
All the tests should work fine.
The garbage collector looks at a number of environment variables which are
the used to affect its operation. These are examined only on Un*x-like
platforms.
GC_INITIAL_HEAP_SIZE=<bytes> - Initial heap size in bytes. May speed up
process start-up.
GC_LOOP_ON_ABORT - Causes the collector abort routine to enter a tight loop.
This may make it easier to debug, such a process, especially
for multithreaded platforms that don't produce usable core
files, or if a core file would be too large. On some
platforms, this also causes SIGSEGV to be caught and
result in an infinite loop in a handler, allowing
similar debugging techniques.
GC_PRINT_STATS - Turn on as much logging as is easily feasible without
adding signifcant runtime overhead. Doesn't work if
the collector is built with SMALL_CONFIG. Overridden
by setting GC_quiet. On by default if the collector
was built without -DSILENT.
GC_PRINT_ADDRESS_MAP - Linux only. Dump /proc/self/maps, i.e. various address
maps for the process, to stderr on every GC. Useful for
mapping root addresses to source for deciphering leak
reports.
GC_NPROCS=<n> - Linux w/threads only. Explicitly sets the number of processors
that the GC should expect to use. Note that setting this to 1
when multiple processors are available will preserve
correctness, but may lead to really horrible performance.
The following turn on runtime flags that are also program settable. Checked
only during initialization. We expect that they will usually be set through
other means, but this may help with debugging and testing:
GC_FIND_LEAK - Turns on GC_find_leak and thus leak detection.
GC_ALL_INTERIOR_POINTERS - Turns on GC_all_interior_pointers and thus interior
pointer recognition.
GC_DONT_GC - Turns off garbage collection. Use cautiously.
Dynamic loading support requires that executables be linked with -ldld.
The alternative is to build the collector without defining DYNAMIC_LOADING
in gcconfig.h and ensuring that all garbage collectable objects are
accessible without considering statically allocated variables in dynamic
libraries.
The collector should compile with either plain cc or cc -Ae. Cc -Aa
fails to define _HPUX_SOURCE and thus will not configure the collector
correctly.
Incremental collection support was reccently added, and should now work.
In spite of past claims, pthread support under HP/UX 11 should now work.
Define GC_HPUX_THREADS for the build. Incremental collection still does not
work in combination with it.
The stack finding code can be confused by putenv calls before collector
initialization. Call GC_malloc or GC_init before any putenv calls.
See README.alpha for Linux on DEC AXP info.
This file applies mostly to Linux/Intel IA32. Ports to Linux on an M68K
and PowerPC are also integrated. They should behave similarly, except that
the PowerPC port lacks incremental GC support, and it is unknown to what
extent the Linux threads code is functional. See below for M68K specific
notes.
Incremental GC is supported on Intel IA32 and M68K.
Dynamic libraries are supported on an ELF system. A static executable
should be linked with the gcc option "-Wl,-defsym,_DYNAMIC=0".
The collector appears to work with Linux threads. We have seen
intermittent hangs in sem_wait. So far we have been unable to reproduce
these unless the process was being debugged or traced. Thus it's
possible that the only real issue is that the debugger loses
signals on rare occasions.
The garbage collector uses SIGPWR and SIGXCPU if it is used with
Linux threads. These should not be touched by the client program.
To use threads, you need to abide by the following requirements:
1) You need to use LinuxThreads (which are included in libc6).
The collector relies on some implementation details of the LinuxThreads
package. It is unlikely that this code will work on other
pthread implementations (in particular it will *not* work with
MIT pthreads).
2) You must compile the collector with -DGC_LINUX_THREADS and -D_REENTRANT
specified in the Makefile.
3a) Every file that makes thread calls should define GC_LINUX_THREADS and
_REENTRANT and then include gc.h. Gc.h redefines some of the
pthread primitives as macros which also provide the collector with
information it requires.
3b) A new alternative to (3a) is to build the collector and compile GC clients
with -DGC_USE_LD_WRAP, and to link the final program with
(for ld) --wrap read --wrap dlopen --wrap pthread_create \
--wrap pthread_join --wrap pthread_detach \
--wrap pthread_sigmask --wrap sleep
(for gcc) -Wl,--wrap -Wl,read -Wl,--wrap -Wl,dlopen -Wl,--wrap \
-Wl,pthread_create -Wl,--wrap -Wl,pthread_join -Wl,--wrap \
-Wl,pthread_detach -Wl,--wrap -Wl,pthread_sigmask \
-Wl,--wrap -Wl,sleep
In any case, _REENTRANT should be defined during compilation.
4) Dlopen() disables collection during its execution. (It can't run
concurrently with the collector, since the collector looks at its
data structures. It can't acquire the allocator lock, since arbitrary
user startup code may run as part of dlopen().) Under unusual
conditions, this may cause unexpected heap growth.
5) The combination of GC_LINUX_THREADS, REDIRECT_MALLOC, and incremental
collection fails in seemingly random places. This hasn't been tracked
down yet, but is perhaps not completely astonishing. The thread package
uses malloc, and thus can presumably get SIGSEGVs while inside the
package. There is no real guarantee that signals are handled properly
at that point.
6) Thread local storage may not be viewed as part of the root set by the
collector. This probably depends on the linuxthreads version. For the
time being, any collectable memory referenced by thread local storage should
also be referenced from elsewhere, or be allocated as uncollectable.
(This is really a bug that should be fixed somehow.)
M68K LINUX:
(From Richard Zidlicky)
The bad news is that it can crash every linux-m68k kernel on a 68040,
so an additional test is needed somewhere on startup. I have meanwhile
patches to correct the problem in 68040 buserror handler but it is not
yet in any standard kernel.
Here is a simple test program to detect whether the kernel has the
problem. It could be run as a separate check in configure or tested
upon startup. If it fails (return !0) than mprotect can't be used
on that system.
/*
* test for bug that may crash 68040 based Linux
*/
#include <sys/mman.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
char *membase;
int pagesize=4096;
int pageshift=12;
int x_taken=0;
int sighandler(int sig)
{
mprotect(membase,pagesize,PROT_READ|PROT_WRITE);
x_taken=1;
}
main()
{
long l;
signal(SIGSEGV,sighandler);
l=(long)mmap(NULL,pagesize,PROT_READ,MAP_PRIVATE | MAP_ANON,-1,0);
if (l==-1)
{
perror("mmap/malloc");
abort();
}
membase=(char*)l;
*(long*)(membase+sizeof(long))=123456789;
if (*(long*)(membase+sizeof(long)) != 123456789 )
{
fprintf(stderr,"writeback failed !\n");
exit(1);
}
if (!x_taken)
{
fprintf(stderr,"exception not taken !\n");
exit(1);
}
fprintf(stderr,"vmtest Ok\n");
exit(0);
}
We have so far failed to find a good way to determine the stack base.
It is highly recommended that GC_stackbottom be set explicitly on program
startup. The supplied value sometimes causes failure under AIX 4.1, though
it appears to work under 3.X. HEURISTIC2 seems to work under 4.1, but
involves a substantial performance penalty, and will fail if there is
no limit on stack size.
There is no thread support. (I assume recent versions of AIX provide
pthreads? I no longer have access to a machine ...)
Performance of the incremental collector can be greatly enhanced with
-DNO_EXECUTE_PERMISSION.
The collector should run with all of the -32, -n32 and -64 ABIs. Remember to
define the AS macro in the Makefile to be "as -64", or "as -n32".
If you use -DREDIRECT_MALLOC=GC_malloc with C++ code, your code should make
at least one explicit call to malloc instead of new to ensure that the proper
version of malloc is linked in.
Sproc threads are not supported in this version, though there may exist other
ports.
Pthreads support is provided. This requires that:
1) You compile the collector with -DGC_IRIX_THREADS specified in the Makefile.
2) You have the latest pthreads patches installed.
(Though the collector makes only documented pthread calls,
it relies on signal/threads interactions working just right in ways
that are not required by the standard. It is unlikely that this code
will run on other pthreads platforms. But please tell me if it does.)
3) Every file that makes thread calls should define IRIX_THREADS and then
include gc.h. Gc.h redefines some of the pthread primitives as macros which
also provide the collector with information it requires.
4) pthread_cond_wait and pthread_cond_timed_wait should be prepared for
premature wakeups. (I believe the pthreads and realted standards require this
anyway. Irix pthreads often terminate a wait if a signal arrives.
The garbage collector uses signals to stop threads.)
5) It is expensive to stop a thread waiting in IO at the time the request is
initiated. Applications with many such threads may not exhibit acceptable
performance with the collector. (Increasing the heap size may help.)
6) The collector should not be compiled with -DREDIRECT_MALLOC. This
confuses some library calls made by the pthreads implementation, which
expect the standard malloc.
The collector supports both incremental collection and threads under
Solaris 2. The incremental collector normally retrieves page dirty information
through the appropriate /proc calls. But it can also be configured
(by defining MPROTECT_VDB instead of PROC_VDB in gcconfig.h) to use mprotect
and signals. This may result in shorter pause times, but it is no longer
safe to issue arbitrary system calls that write to the heap.
Under other UNIX versions,
the collector normally obtains memory through sbrk. There is some reason
to expect that this is not safe if the client program also calls the system
malloc, or especially realloc. The sbrk man page strongly suggests this is
not safe: "Many library routines use malloc() internally, so use brk()
and sbrk() only when you know that malloc() definitely will not be used by
any library routine." This doesn't make a lot of sense to me, since there
seems to be no documentation as to which routines can transitively call malloc.
Nonetheless, under Solaris2, the collector now (since 4.12) allocates
memory using mmap by default. (It defines USE_MMAP in gcconfig.h.)
You may want to reverse this decisions if you use -DREDIRECT_MALLOC=...
SOLARIS THREADS:
The collector must be compiled with -DGC_SOLARIS_THREADS (thr_ functions)
or -DGC_SOLARIS_PTHREADS (pthread_ functions) to be thread safe.
It is also essential that gc.h be included in files that call thr_create,
thr_join, thr_suspend, thr_continue, or dlopen. Gc.h macro defines
these to also do GC bookkeeping, etc. Gc.h must be included with
one or both of these macros defined, otherwise
these replacements are not visible.
A collector built in this way way only be used by programs that are
linked with the threads library.
In this mode, the collector contains various workarounds for older Solaris
bugs. Mostly, these should not be noticeable unless you look at system
call traces. However, it cannot protect a guard page at the end of
a thread stack. If you know that you will only be running Solaris2.5
or later, it should be possible to fix this by compiling the collector
with -DSOLARIS23_MPROTECT_BUG_FIXED.
Since 5.0 alpha5, dlopen disables collection temporarily,
unless USE_PROC_FOR_LIBRARIES is defined. In some unlikely cases, this
can result in unpleasant heap growth. But it seems better than the
race/deadlock issues we had before.
If solaris_threads are used on an X86 processor with malloc redirected to
GC_malloc, it is necessary to call GC_thr_init explicitly before forking the
first thread. (This avoids a deadlock arising from calling GC_thr_init
with the allocation lock held.)
It appears that there is a problem in using gc_cpp.h in conjunction with
Solaris threads and Sun's C++ runtime. Apparently the overloaded new operator
is invoked by some iostream initialization code before threads are correctly
initialized. As a result, call to thr_self() in garbage collector
initialization segfaults. Currently the only known workaround is to not
invoke the garbage collector from a user defined global operator new, or to
have it invoke the garbage-collector's allocators only after main has started.
(Note that the latter requires a moderately expensive test in operator
delete.)
Hans-J. Boehm
(The above contains my personal opinions, which are probably not shared
by anyone else.)
Alistair Crooks supplied the port. He used Lexa C version 2.1.3 with
-Xa to compile.
The collector has at various times been compiled under Windows 95 & NT,
with the original Microsoft SDK, with Visual C++ 2.0, 4.0, and 6, with
the GNU win32 environment, with Borland 4.5, and recently with
Watcom C. It is likely that some of these have been broken in the
meantime. Patches are appreciated.
It runs under both win32s and win32, but with different semantics.
Under win32, all writable pages outside of the heaps and stack are
scanned for roots. Thus the collector sees pointers in DLL data
segments. Under win32s, only the main data segment is scanned.
(The main data segment should always be scanned. Under some
versions of win32s, other regions may also be scanned.)
Thus all accessible objects should be accessible from local variables
or variables in the main data segment. Alternatively, other data
segments (e.g. in DLLs) may be registered with the collector by
calling GC_init() and then GC_register_root_section(a), where
a is the address of some variable inside the data segment. (Duplicate
registrations are ignored, but not terribly quickly.)
(There are two reasons for this. We didn't want to see many 16:16
pointers. And the VirtualQuery call has different semantics under
the two systems, and under different versions of win32s.)
The collector test program "gctest" is linked as a GUI application,
but does not open any windows. Its output appears in the file
"gc.log". It may be started from the file manager. The hour glass
cursor may appear as long as it's running. If it is started from the
command line, it will usually run in the background. Wait a few
minutes (a few seconds on a modern machine) before you check the output.
You should see either a failure indication or a "Collector appears to
work" message.
The cord test program has not been ported (but should port
easily). A toy editor (cord/de.exe) based on cords (heavyweight
strings represented as trees) has been ported and is included.
It runs fine under either win32 or win32S. It serves as an example
of a true Windows application, except that it was written by a
nonexpert Windows programmer. (There are some peculiarities
in the way files are displayed. The <cr> is displayed explicitly
for standard DOS text files. As in the UNIX version, control
characters are displayed explicitly, but in this case as red text.
This may be suboptimal for some tastes and/or sets of default
window colors.)
In general -DREDIRECT_MALLOC is unlikely to work unless the
application is completely statically linked.
For Microsoft development tools, rename NT_MAKEFILE as
MAKEFILE. (Make sure that the CPU environment variable is defined
to be i386.) In order to use the gc_cpp.h C++ interface, all
client code should include gc_cpp.h.
Clients may need to define GC_NOT_DLL before including gc.h, if the
collector was built as a static library (as it normally is in the
absence of thread support).
For GNU-win32, use the regular makefile, possibly after uncommenting
the line "include Makefile.DLLs". The latter should be necessary only
if you want to package the collector as a DLL. The GNU-win32 port is
believed to work only for b18, not b19, probably dues to linker changes
in b19. This is probably fixable with a different definition of
DATASTART and DATAEND in gcconfig.h.
For Borland tools, use BCC_MAKEFILE. Note that
Borland's compiler defaults to 1 byte alignment in structures (-a1),
whereas Visual C++ appears to default to 8 byte alignment (/Zp8).
The garbage collector in its default configuration EXPECTS AT
LEAST 4 BYTE ALIGNMENT. Thus the BORLAND DEFAULT MUST
BE OVERRIDDEN. (In my opinion, it should usually be anyway.
I expect that -a1 introduces major performance penalties on a
486 or Pentium.) Note that this changes structure layouts. (As a last
resort, gcconfig.h can be changed to allow 1 byte alignment. But
this has significant negative performance implications.)
The Makefile is set up to assume Borland 4.5. If you have another
version, change the line near the top. By default, it does not
require the assembler. If you do have the assembler, I recommend
removing the -DUSE_GENERIC.
There is some support for incremental collection. This is
currently pretty simple-minded. Pages are protected. Protection
faults are caught by a handler installed at the bottom of the handler
stack. This is both slow and interacts poorly with a debugger.
Whenever possible, I recommend adding a call to
GC_enable_incremental at the last possible moment, after most
debugging is complete. Unlike the UNIX versions, no system
calls are wrapped by the collector itself. It may be necessary
to wrap ReadFile calls that use a buffer in the heap, so that the
call does not encounter a protection fault while it's running.
(As usual, none of this is an issue unless GC_enable_incremental
is called.)
Note that incremental collection is disabled with -DSMALL_CONFIG.
James Clark has contributed the necessary code to support win32 threads.
Use NT_THREADS_MAKEFILE (a.k.a gc.mak) instead of NT_MAKEFILE
to build this version. Note that this requires some files whose names
are more than 8 + 3 characters long. Thus you should unpack the tar file
so that long file names are preserved. To build the garbage collector
test with VC++ from the command line, use
nmake /F ".\gc.mak" CFG="gctest - Win32 Release"
This requires that the subdirectory gctest\Release exist.
The test program and DLL will reside in the Release directory.
This version relies on the collector residing in a dll.
This version currently supports incremental collection only if it is
enabled before any additional threads are created.
Version 4.13 attempts to fix some of the earlier problems, but there
may be other issues. If you need solid support for win32 threads, you
might check with Geodesic Systems. Their collector must be licensed,
but they have invested far more time in win32-specific issues.
Hans
Ivan V. Demakov's README for the Watcom port:
The collector has been compiled with Watcom C 10.6 and 11.0.
It runs under win32, win32s, and even under msdos with dos4gw
dos-extender. It should also run under OS/2, though this isn't
tested. Under win32 the collector can be built either as dll
or as static library.
Note that all compilations were done under Windows 95 or NT.
For unknown reason compiling under Windows 3.11 for NT (one
attempt has been made) leads to broken executables.
Incremental collection is not supported.
cord is not ported.
Before compiling you may need to edit WCC_MAKEFILE to set target
platform, library type (dynamic or static), calling conventions, and
optimization options.
To compile the collector and testing programs use the command:
wmake -f WCC_MAKEFILE
All programs using gc should be compiled with 4-byte alignment.
For further explanations on this see comments about Borland.
If gc compiled as dll, the macro ``GC_DLL'' should be defined before
including "gc.h" (for example, with -DGC_DLL compiler option). It's
important, otherwise resulting programs will not run.
Ivan Demakov (email: ivan@tgrad.nsk.su)
This is an ASCII diagram of the data structure used to check pointer
validity. It was provided by Dave Barrett <barrett@asgard.cs.colorado.edu>,
and should be of use to others attempting to understand the code.
The data structure in GC4.X is essentially the same. -HB
Data Structure used by GC_base in gc3.7:
21-Apr-94
63 LOG_TOP_SZ[11] LOG_BOTTOM_SZ[10] LOG_HBLKSIZE[13]
+------------------+----------------+------------------+------------------+
p:| | TL_HASH(hi) | | HBLKDISPL(p) |
+------------------+----------------+------------------+------------------+
\-----------------------HBLKPTR(p)-------------------/
\------------hi-------------------/
\______ ________/ \________ _______/ \________ _______/
V V V
| | |
GC_top_index[] | | |
--- +--------------+ | | |
^ | | | | |
| | | | | |
TOP +--------------+<--+ | |
_SZ +-<| [] | * | |
(items)| +--------------+ if 0 < bi< HBLKSIZE | |
| | | | then large object | |
| | | | starts at the bi'th | |
v | | | HBLK before p. | i |
--- | +--------------+ | (word- |
v | aligned) |
bi= |GET_BI(p){->hash_link}->key==hi | |
v | |
| (bottom_index) \ scratch_alloc'd | |
| ( struct bi ) / by get_index() | |
--- +->+--------------+ | |
^ | | | |
^ | | | |
BOTTOM | | ha=GET_HDR_ADDR(p) | |
_SZ(items)+--------------+<----------------------+ +-------+
| +--<| index[] | |
| | +--------------+ GC_obj_map: v
| | | | from / +-+-+-----+-+-+-+-+ ---
v | | | GC_add < 0| | | | | | | | ^
--- | +--------------+ _map_entry \ +-+-+-----+-+-+-+-+ |
| | asc_link | +-+-+-----+-+-+-+-+ MAXOBJSZ
| +--------------+ +-->| | | j | | | | | +1
| | key | | +-+-+-----+-+-+-+-+ |
| +--------------+ | +-+-+-----+-+-+-+-+ |
| | hash_link | | | | | | | | | | v
| +--------------+ | +-+-+-----+-+-+-+-+ ---
| | |<--MAX_OFFSET--->|
| | (bytes)
HDR(p)| GC_find_header(p) | |<--MAP_ENTRIES-->|
| \ from | =HBLKSIZE/WORDSZ
| (hdr) (struct hblkhdr) / alloc_hdr() | (1024 on Alpha)
+-->+----------------------+ | (8/16 bits each)
GET_HDR(p)| word hb_sz (words) | |
+----------------------+ |
| struct hblk *hb_next | |
+----------------------+ |
|mark_proc hb_mark_proc| |
+----------------------+ |
| char * hb_map |>-------------+
+----------------------+
| ushort hb_obj_kind |
+----------------------+
| hb_last_reclaimed |
--- +----------------------+
^ | |
MARK_BITS| hb_marks[] | *if hdr is free, hb_sz + DISCARD_WORDS
_SZ(words)| | is the size of a heap chunk (struct hblk)
v | | of at least MININCR*HBLKSIZE bytes (below),
--- +----------------------+ otherwise, size of each object in chunk.
Dynamic data structures above are interleaved throughout the heap in blocks of
size MININCR * HBLKSIZE bytes as done by gc_scratch_alloc which cannot be
freed; free lists are used (e.g. alloc_hdr). HBLKs's below are collected.
(struct hblk)
--- +----------------------+ < HBLKSIZE --- --- DISCARD_
^ |garbage[DISCARD_WORDS]| aligned ^ ^ HDR_BYTES WORDS
| | | | v (bytes) (words)
| +-----hb_body----------+ < WORDSZ | --- ---
| | | aligned | ^ ^
| | Object 0 | | hb_sz |
| | | i |(word- (words)|
| | | (bytes)|aligned) v |
| + - - - - - - - - - - -+ --- | --- |
| | | ^ | ^ |
n * | | j (words) | hb_sz BODY_SZ
HBLKSIZE | Object 1 | v v | (words)
(bytes) | |--------------- v MAX_OFFSET
| + - - - - - - - - - - -+ --- (bytes)
| | | !All_INTERIOR_PTRS ^ |
| | | sets j only for hb_sz |
| | Object N | valid object offsets. | |
v | | All objects WORDSZ v v
--- +----------------------+ aligned. --- ---
DISCARD_WORDS is normally zero. Indeed the collector has not been tested
with another value in ages.
.TH GC_MALLOC 1L "12 February 1996"
.SH NAME
GC_malloc, GC_malloc_atomic, GC_free, GC_realloc, GC_enable_incremental, GC_register_finalizer, GC_malloc_ignore_off_page, GC_malloc_atomic_ignore_off_page, GC_set_warn_proc \- Garbage collecting malloc replacement
.SH SYNOPSIS
#include "gc.h"
.br
# define malloc(n) GC_malloc(n)
.br
... malloc(...) ...
.br
.sp
cc ... gc.a
.LP
.SH DESCRIPTION
.I GC_malloc
and
.I GC_free
are plug-in replacements for standard malloc and free. However,
.I
GC_malloc
will attempt to reclaim inaccessible space automatically by invoking a conservative garbage collector at appropriate points. The collector traverses all data structures accessible by following pointers from the machines registers, stack(s), data, and bss segments. Inaccessible structures will be reclaimed. A machine word is considered to be a valid pointer if it is an address inside an object allocated by
.I
GC_malloc
or friends.
.LP
See the documentation in the include file gc_cpp.h for an alternate, C++ specific interface to the garbage collector.
.LP
Unlike the standard implementations of malloc,
.I
GC_malloc
clears the newly allocated storage.
.I
GC_malloc_atomic
does not. Furthermore, it informs the collector that the resulting object will never contain any pointers, and should therefore not be scanned by the collector.
.LP
.I
GC_free
can be used to deallocate objects, but its use is optional, and generally discouraged.
.I
GC_realloc
has the standard realloc semantics. It preserves pointer-free-ness.
.I
GC_register_finalizer
allows for registration of functions that are invoked when an object becomes inaccessible.
.LP
The garbage collector tries to avoid allocating memory at locations that already appear to be referenced before allocation. (Such apparent ``pointers'' are usually large integers and the like that just happen to look like an address.) This may make it hard to allocate very large objects. An attempt to do so may generate a warning.
.LP
.I
GC_malloc_ignore_off_page
and
.I
GC_malloc_atomic_ignore_off_page
inform the collector that the client code will always maintain a pointer to near the beginning of the object (within the first 512 bytes), and that pointers beyond that can be ignored by the collector. This makes it much easier for the collector to place large objects. These are recommended for large object allocation. (Objects expected to be larger than about 100KBytes should be allocated this way.)
.LP
It is also possible to use the collector to find storage leaks in programs destined to be run with standard malloc/free. The collector can be compiled for thread-safe operation. Unlike standard malloc, it is safe to call malloc after a previous malloc call was interrupted by a signal, provided the original malloc call is not resumed.
.LP
The collector may, on rare occasion produce warning messages. On UNIX machines these appear on stderr. Warning messages can be filtered, redirected, or ignored with
.I
GC_set_warn_proc.
This is recommended for production code. See gc.h for details.
.LP
Debugging versions of many of the above routines are provided as macros. Their names are identical to the above, but consist of all capital letters. If GC_DEBUG is defined before gc.h is included, these routines do additional checking, and allow the leak detecting version of the collector to produce slightly more useful output. Without GC_DEBUG defined, they behave exactly like the lower-case versions.
.LP
On some machines, collection will be performed incrementally after a call to
.I
GC_enable_incremental.
This may temporarily write protect pages in the heap. See the README file for more information on how this interacts with system calls that write to the heap.
.LP
Other facilities not discussed here include limited facilities to support incremental collection on machines without appropriate VM support, provisions for providing more explicit object layout information to the garbage collector, more direct support for ``weak'' pointers, support for ``abortable'' garbage collections during idle time, etc.
.LP
.SH "SEE ALSO"
The README and gc.h files in the distribution. More detailed definitions of the functions exported by the collector are given there. (The above list is not complete.)
.LP
Boehm, H., and M. Weiser, "Garbage Collection in an Uncooperative Environment",
\fISoftware Practice & Experience\fP, September 1988, pp. 807-820.
.LP
The malloc(3) man page.
.LP
.SH AUTHOR
Hans-J. Boehm (boehm@parc.xerox.com). Some of the code was written by others, most notably Alan Demers.
// Visual C++ seems to prefer a .cpp extension to .cc
#include "gc_cpp.cc"
/*
* Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
* Copyright (c) 1997 by Silicon Graphics. All rights reserved.
* Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*
* Original author: Bill Janssen
* Heavily modified by Hans Boehm and others
*/
/*
* This used to be in dyn_load.c. It was extracted into a separate file
* to avoid having to link against libdl.{a,so} if the client doesn't call
* dlopen. -HB
*/
#include "private/gc_priv.h"
# if defined(LINUX_THREADS) || defined(SOLARIS_THREADS) \
|| defined(HPUX_THREADS) || defined(IRIX_THREADS)
# if defined(dlopen) && !defined(GC_USE_LD_WRAP)
/* To support various threads pkgs, gc.h interposes on dlopen by */
/* defining "dlopen" to be "GC_dlopen", which is implemented below. */
/* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the */
/* real system dlopen() in their implementation. We first remove */
/* gc.h's dlopen definition and restore it later, after GC_dlopen(). */
# undef dlopen
# endif
/* Make sure we're not in the middle of a collection, and make */
/* sure we don't start any. Returns previous value of GC_dont_gc. */
/* This is invoked prior to a dlopen call to avoid synchronization */
/* issues. We can't just acquire the allocation lock, since startup */
/* code in dlopen may try to allocate. */
/* This solution risks heap growth in the presence of many dlopen */
/* calls in either a multithreaded environment, or if the library */
/* initialization code allocates substantial amounts of GC'ed memory. */
/* But I don't know of a better solution. */
/* This can still deadlock if the client explicitly starts a GC */
/* during the dlopen. He shouldn't do that. */
static GC_bool disable_gc_for_dlopen()
{
GC_bool result;
LOCK();
result = GC_dont_gc;
while (GC_incremental && GC_collection_in_progress()) {
GC_collect_a_little_inner(1000);
}
GC_dont_gc = TRUE;
UNLOCK();
return(result);
}
/* Redefine dlopen to guarantee mutual exclusion with */
/* GC_register_dynamic_libraries. */
/* Should probably happen for other operating systems, too. */
#include <dlfcn.h>
#ifdef GC_USE_LD_WRAP
void * __wrap_dlopen(const char *path, int mode)
#else
void * GC_dlopen(path, mode)
GC_CONST char * path;
int mode;
#endif
{
void * result;
GC_bool dont_gc_save;
# ifndef USE_PROC_FOR_LIBRARIES
dont_gc_save = disable_gc_for_dlopen();
# endif
# ifdef GC_USE_LD_WRAP
result = (void *)__real_dlopen(path, mode);
# else
result = dlopen(path, mode);
# endif
# ifndef USE_PROC_FOR_LIBRARIES
GC_dont_gc = dont_gc_save;
# endif
return(result);
}
# endif /* LINUX_THREADS || SOLARIS_THREADS || ... */
#ifndef GC_AMIGA_REDIRECTS_H
# define GC_AMIGA_REDIRECTS_H
# if ( defined(_AMIGA) && !defined(GC_AMIGA_MAKINGLIB) )
extern void *GC_amiga_realloc(void *old_object,size_t new_size_in_bytes);
# define GC_realloc(a,b) GC_amiga_realloc(a,b)
extern void GC_amiga_set_toany(void (*func)(void));
extern int GC_amiga_free_space_divisor_inc;
extern void *(*GC_amiga_allocwrapper_do) \
(size_t size,void *(*AllocFunction)(size_t size2));
# define GC_malloc(a) \
(*GC_amiga_allocwrapper_do)(a,GC_malloc)
# define GC_malloc_atomic(a) \
(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic)
# define GC_malloc_uncollectable(a) \
(*GC_amiga_allocwrapper_do)(a,GC_malloc_uncollectable)
# define GC_malloc_stubborn(a) \
(*GC_amiga_allocwrapper_do)(a,GC_malloc_stubborn)
# define GC_malloc_atomic_uncollectable(a) \
(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_uncollectable)
# define GC_malloc_ignore_off_page(a) \
(*GC_amiga_allocwrapper_do)(a,GC_malloc_ignore_off_page)
# define GC_malloc_atomic_ignore_off_page(a) \
(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page)
# endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */
#endif /* GC_AMIGA_REDIRECTS_H */
/*
* This is a simple API to implement pointer back tracing, i.e.
* to answer questions such as "who is pointing to this" or
* "why is this object being retained by the collector"
*
* This API assumes that we have an ANSI C compiler.
*
* Most of these calls yield useful information on only after
* a garbage collection. Usually the client will first force
* a full collection and then gather information, preferably
* before much intervening allocation.
*
* The implementation of the interface is only about 99.9999%
* correct. It is intended to be good enough for profiling,
* but is not intended to be used with production code.
*
* Results are likely to be much more useful if all allocation is
* accomplished through the debugging allocators.
*
* The implementation idea is due to A. Demers.
*/
#ifndef GC_BACKPTR_H
#define GC_BACKPTR_H
/* Store information about the object referencing dest in *base_p */
/* and *offset_p. */
/* If multiple objects or roots point to dest, the one reported */
/* will be the last on used by the garbage collector to trace the */
/* object. */
/* source is root ==> *base_p = address, *offset_p = 0 */
/* source is heap object ==> *base_p != 0, *offset_p = offset */
/* Returns 1 on success, 0 if source couldn't be determined. */
/* Dest can be any address within a heap object. */
typedef enum { GC_UNREFERENCED, /* No reference info available. */
GC_NO_SPACE, /* Dest not allocated with debug alloc */
GC_REFD_FROM_ROOT, /* Referenced directly by root *base_p */
GC_REFD_FROM_REG, /* Referenced from a register, i.e. */
/* a root without an address. */
GC_REFD_FROM_HEAP, /* Referenced from another heap obj. */
GC_FINALIZER_REFD /* Finalizable and hence accessible. */
} GC_ref_kind;
GC_ref_kind GC_get_back_ptr_info(void *dest, void **base_p, size_t *offset_p);
/* Generate a random heap address. */
/* The resulting address is in the heap, but */
/* not necessarily inside a valid object. */
void * GC_generate_random_heap_address(void);
/* Generate a random address inside a valid marked heap object. */
void * GC_generate_random_valid_address(void);
/* Force a garbage collection and generate a backtrace from a */
/* random heap address. */
/* This uses the GC logging mechanism (GC_printf) to produce */
/* output. It can often be called from a debugger. The */
/* source in dbg_mlc.c also serves as a sample client. */
void GC_generate_random_backtrace(void);
/* Print a backtrace from a specific address. Used by the */
/* above. The client should call GC_gcollect() immediately */
/* before invocation. */
void GC_print_backtrace(void *);
#endif /* GC_BACKPTR_H */
/*
* Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
/*
* Interface for thread local allocation. Memory obtained
* this way can be used by all threads, as though it were obtained
* from an allocator like GC_malloc. The difference is that GC_local_malloc
* counts the number of allocations of a given size from the current thread,
* and uses GC_malloc_many to perform the allocations once a threashold
* is exceeded. Thus far less synchronization may be needed.
* Allocation of known large objects should not use this interface.
* This interface is designed primarily for fast allocation of small
* objects on multiprocessors, e.g. for a JVM running on an MP server.
*
* If this file is included with GC_GCJ_SUPPORT defined, GCJ-style
* bitmap allocation primitives will also be included.
*
* If this file is included with GC_REDIRECT_TO_LOCAL defined, then
* GC_MALLOC, GC_MALLOC_ATOMIC, and possibly GC_GCJ_MALLOC will
* be redefined to use the thread local allocatoor.
*
* The interface is available only if the collector is built with
* -DTHREAD_LOCAL_ALLOC, which is currently supported only on Linux.
*
* The debugging allocators use standard, not thread-local allocation.
*/
#ifndef GC_LOCAL_ALLOC_H
#define GC_LOCAL_ALLOC_H
#ifndef _GC_H
# include "gc.h"
#endif
#if defined(GC_GCJ_SUPPORT) && !defined(GC_GCJ_H)
# include "gc_gcj.h"
#endif
/* We assume ANSI C for this interface. */
GC_PTR GC_local_malloc(size_t bytes);
GC_PTR GC_local_malloc_atomic(size_t bytes);
#if defined(GC_GCJ_SUPPORT)
GC_PTR GC_local_gcj_malloc(size_t bytes,
void * ptr_to_struct_containing_descr);
#endif
# ifdef GC_DEBUG
# define GC_LOCAL_MALLOC(s) GC_debug_malloc(s,GC_EXTRAS)
# define GC_LOCAL_MALLOC_ATOMIC(s) GC_debug_malloc_atomic(s,GC_EXTRAS)
# ifdef GC_GCJ_SUPPORT
# define GC_LOCAL_GCJ_MALLOC(s,d) GC_debug_gcj_malloc(s,d,GC_EXTRAS)
# endif
# else
# define GC_LOCAL_MALLOC(s) GC_local_malloc(s)
# define GC_LOCAL_MALLOC_ATOMIC(s) GC_local_malloc_atomic(s)
# ifdef GC_GCJ_SUPPORT
# define GC_LOCAL_GCJ_MALLOC(s,d) GC_local_gcj_malloc(s,d)
# endif
# endif
# ifdef GC_REDIRECT_TO_LOCAL
# undef GC_MALLOC
# define GC_MALLOC(s) GC_LOCAL_MALLOC(s)
# undef GC_MALLOC_ATOMIC
# define GC_MALLOC_ATOMIC(s) GC_LOCAL_MALLOC_ATOMIC(s)
# ifdef GC_GCJ_SUPPORT
# undef GC_GCJ_MALLOC
# define GC_GCJ_MALLOC(s,d) GC_LOCAL_GCJ_MALLOC(s,d)
# endif
# endif
#endif /* GC_LOCAL_ALLOC_H */
/*
* Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved.
* Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*
*/
/*
* This contains interfaces to the GC marker that are likely to be useful to
* clients that provide detailed heap layout information to the collector.
* This interface should not be used by normal C or C++ clients.
* It will be useful to runtimes for other languages.
*
* Note that this file is not "namespace-clean", i.e. it introduces names
* not prefixed with GC_, which may collide with the client's names. It
* should be included only in those few places that directly provide
* information to the collector.
*/
#ifndef GC_MARK_H
# define GC_MARK_H
# ifndef GC_H
# include "gc.h"
# endif
/* A client supplied mark procedure. Returns new mark stack pointer. */
/* Primary effect should be to push new entries on the mark stack. */
/* Mark stack pointer values are passed and returned explicitly. */
/* Global variables decribing mark stack are not necessarily valid. */
/* (This usually saves a few cycles by keeping things in registers.) */
/* Assumed to scan about GC_PROC_BYTES on average. If it needs to do */
/* much more work than that, it should do it in smaller pieces by */
/* pushing itself back on the mark stack. */
/* Note that it should always do some work (defined as marking some */
/* objects) before pushing more than one entry on the mark stack. */
/* This is required to ensure termination in the event of mark stack */
/* overflows. */
/* This procedure is always called with at least one empty entry on the */
/* mark stack. */
/* Currently we require that mark procedures look for pointers in a */
/* subset of the places the conservative marker would. It must be safe */
/* to invoke the normal mark procedure instead. */
/* WARNING: Such a mark procedure may be invoked on an unused object */
/* residing on a free list. Such objects are cleared, except for a */
/* free list link field in the first word. Thus mark procedures may */
/* not count on the presence of a type descriptor, and must handle this */
/* case correctly somehow. */
# define GC_PROC_BYTES 100
struct GC_ms_entry;
typedef struct GC_ms_entry * (*GC_mark_proc) GC_PROTO((
GC_word * addr, struct GC_ms_entry * mark_stack_ptr,
struct GC_ms_entry * mark_stack_limit, GC_word env));
# define GC_LOG_MAX_MARK_PROCS 6
# define GC_MAX_MARK_PROCS (1 << GC_LOG_MAX_MARK_PROCS)
/* In a few cases it's necessary to assign statically known indices to */
/* certain mark procs. Thus we reserve a few for well known clients. */
/* (This is necessary if mark descriptors are compiler generated.) */
#define GC_RESERVED_MARK_PROCS 8
# define GC_GCJ_RESERVED_MARK_PROC_INDEX 0
/* Object descriptors on mark stack or in objects. Low order two */
/* bits are tags distinguishing among the following 4 possibilities */
/* for the high order 30 bits. */
#define GC_DS_TAG_BITS 2
#define GC_DS_TAGS ((1 << GC_DS_TAG_BITS) - 1)
#define GC_DS_LENGTH 0 /* The entire word is a length in bytes that */
/* must be a multiple of 4. */
#define GC_DS_BITMAP 1 /* 30 (62) bits are a bitmap describing pointer */
/* fields. The msb is 1 iff the first word */
/* is a pointer. */
/* (This unconventional ordering sometimes */
/* makes the marker slightly faster.) */
/* Zeroes indicate definite nonpointers. Ones */
/* indicate possible pointers. */
/* Only usable if pointers are word aligned. */
#define GC_DS_PROC 2
/* The objects referenced by this object can be */
/* pushed on the mark stack by invoking */
/* PROC(descr). ENV(descr) is passed as the */
/* last argument. */
# define GC_MAKE_PROC(proc_index, env) \
(((((env) << GC_LOG_MAX_MARK_PROCS) \
| (proc_index)) << GC_DS_TAG_BITS) | GC_DS_PROC)
#define GC_DS_PER_OBJECT 3 /* The real descriptor is at the */
/* byte displacement from the beginning of the */
/* object given by descr & ~DS_TAGS */
/* If the descriptor is negative, the real */
/* descriptor is at (*<object_start>) - */
/* (descr & ~DS_TAGS) - GC_INDIR_PER_OBJ_BIAS */
/* The latter alternative can be used if each */
/* object contains a type descriptor in the */
/* first word. */
/* Note that in multithreaded environments */
/* per object descriptors maust be located in */
/* either the first two or last two words of */
/* the object, since only those are guaranteed */
/* to be cleared while the allocation lock is */
/* held. */
#define GC_INDIR_PER_OBJ_BIAS 0x10
extern GC_PTR GC_least_plausible_heap_addr;
extern GC_PTR GC_greatest_plausible_heap_addr;
/* Bounds on the heap. Guaranteed valid */
/* Likely to include future heap expansion. */
/* Handle nested references in a custom mark procedure. */
/* Check if obj is a valid object. If so, ensure that it is marked. */
/* If it was not previously marked, push its contents onto the mark */
/* stack for future scanning. The object will then be scanned using */
/* its mark descriptor. */
/* Returns the new mark stack pointer. */
/* Handles mark stack overflows correctly. */
/* Since this marks first, it makes progress even if there are mark */
/* stack overflows. */
/* Src is the address of the pointer to obj, which is used only */
/* for back pointer-based heap debugging. */
/* It is strongly recommended that most objects be handled without mark */
/* procedures, e.g. with bitmap descriptors, and that mark procedures */
/* be reserved for exceptional cases. That will ensure that */
/* performance of this call is not extremely performance critical. */
/* (Otherwise we would need to inline GC_mark_and_push completely, */
/* which would tie the client code to a fixed colllector version.) */
struct GC_ms_entry *GC_mark_and_push
GC_PROTO((GC_PTR obj,
struct GC_ms_entry * mark_stack_ptr,
struct GC_ms_entry * mark_stack_limit, GC_PTR *src));
#define GC_MARK_AND_PUSH(obj, msp, lim, src) \
(((GC_word)obj >= (GC_word)GC_least_plausible_heap_addr && \
(GC_word)obj <= (GC_word)GC_greatest_plausible_heap_addr)? \
GC_mark_and_push(obj, msp, lim, src) : \
msp)
#endif /* GC_MARK_H */
/* Our pthread support normally needs to intercept a number of thread */
/* calls. We arrange to do that here, if appropriate. */
#ifndef GC_PTHREAD_REDIRECTS_H
#define GC_PTHREAD_REDIRECTS_H
#if defined(GC_SOLARIS_THREADS)
/* We need to intercept calls to many of the threads primitives, so */
/* that we can locate thread stacks and stop the world. */
/* Note also that the collector cannot see thread specific data. */
/* Thread specific data should generally consist of pointers to */
/* uncollectable objects (allocated with GC_malloc_uncollectable, */
/* not the system malloc), which are deallocated using the destructor */
/* facility in thr_keycreate. Alternatively, keep a redundant pointer */
/* to thread specific data on the thread stack. */
# include <thread.h>
int GC_thr_create(void *stack_base, size_t stack_size,
void *(*start_routine)(void *), void *arg, long flags,
thread_t *new_thread);
int GC_thr_join(thread_t wait_for, thread_t *departed, void **status);
int GC_thr_suspend(thread_t target_thread);
int GC_thr_continue(thread_t target_thread);
void * GC_dlopen(const char *path, int mode);
# define thr_create GC_thr_create
# define thr_join GC_thr_join
# define thr_suspend GC_thr_suspend
# define thr_continue GC_thr_continue
#endif /* GC_SOLARIS_THREADS */
#if defined(GC_SOLARIS_PTHREADS)
# include <pthread.h>
# include <signal.h>
extern int GC_pthread_create(pthread_t *new_thread,
const pthread_attr_t *attr,
void * (*thread_execp)(void *), void *arg);
extern int GC_pthread_join(pthread_t wait_for, void **status);
# define pthread_join GC_pthread_join
# define pthread_create GC_pthread_create
#endif
#if defined(GC_SOLARIS_PTHREADS) || defined(GC_SOLARIS_THREADS)
# define dlopen GC_dlopen
#endif /* SOLARIS_THREADS || SOLARIS_PTHREADS */
#if !defined(GC_USE_LD_WRAP) && \
(defined(GC_IRIX_THREADS) || defined(GC_LINUX_THREADS) \
|| defined(GC_HPUX_THREADS) || defined(GC_OSF1_THREADS))
/* We treat these similarly. */
# include <pthread.h>
# include <signal.h>
int GC_pthread_create(pthread_t *new_thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *), void *arg);
int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset);
int GC_pthread_join(pthread_t thread, void **retval);
int GC_pthread_detach(pthread_t thread);
# define pthread_create GC_pthread_create
# define pthread_sigmask GC_pthread_sigmask
# define pthread_join GC_pthread_join
# define pthread_detach GC_pthread_detach
# define dlopen GC_dlopen
#endif /* GC_xxxxx_THREADS */
#endif /* GC_PTHREAD_REDIRECTS_H */
/*
* Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers
* Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved.
* Copyright (c) 1997 by Silicon Graphics. All rights reserved.
* Copyright (c) 1999 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
/*
* This is mostly an internal header file. Typical clients should
* not use it. Clients that define their own object kinds with
* debugging allocators will probably want to include this, however.
* No attempt is made to keep the namespace clean. This should not be
* included from header filrd that are frequently included by clients.
*/
#ifndef _DBG_MLC_H
#define _DBG_MLC_H
# define I_HIDE_POINTERS
# include "gc_priv.h"
# ifdef KEEP_BACK_PTRS
# include "gc_backptr.h"
# endif
# define START_FLAG ((word)0xfedcedcb)
# define END_FLAG ((word)0xbcdecdef)
/* Stored both one past the end of user object, and one before */
/* the end of the object as seen by the allocator. */
/* Object header */
typedef struct {
# ifdef KEEP_BACK_PTRS
GC_hidden_pointer oh_back_ptr;
/* We make sure that we only store even valued */
/* pointers here, so that the hidden version has */
/* the least significant bit set. We never */
/* overwrite a value with the least significant */
/* bit clear, thus ensuring that we never overwrite */
/* a free list link field. */
/* The following are special back pointer values. */
/* Note that the "hidden" (i.e. bitwise */
/* complemented version) of these is actually */
/* stored. */
# define NOT_MARKED (ptr_t)(0)
# define MARKED_FOR_FINALIZATION (ptr_t)(2)
/* Object was marked because it is finalizable. */
# define MARKED_FROM_REGISTER (ptr_t)(4)
/* Object was marked from a rgister. Hence the */
/* source of the reference doesn't have an address. */
# if ALIGNMENT == 1
/* Fudge back pointer to be even. */
# define HIDE_BACK_PTR(p) HIDE_POINTER(~1 & (GC_word)(p))
# else
# define HIDE_BACK_PTR(p) HIDE_POINTER(p)
# endif
# ifdef ALIGN_DOUBLE
word oh_dummy;
# endif
# endif
char * oh_string; /* object descriptor string */
word oh_int; /* object descriptor integers */
# ifdef NEED_CALLINFO
struct callinfo oh_ci[NFRAMES];
# endif
# ifndef SHORT_DBG_HDRS
word oh_sz; /* Original malloc arg. */
word oh_sf; /* start flag */
# endif /* SHORT_DBG_HDRS */
} oh;
/* The size of the above structure is assumed not to dealign things, */
/* and to be a multiple of the word length. */
#define DEBUG_BYTES (sizeof (oh) + sizeof (word))
#define USR_PTR_FROM_BASE(p) ((ptr_t)(p) + sizeof(oh))
/* There is no reason to ever add a byte at the end explicitly, since we */
/* already add a guard word. */
#undef ROUNDED_UP_WORDS
#define ROUNDED_UP_WORDS(n) BYTES_TO_WORDS((n) + WORDS_TO_BYTES(1) - 1)
#ifdef SAVE_CALL_CHAIN
# define ADD_CALL_CHAIN(base, ra) GC_save_callers(((oh *)(base)) -> oh_ci)
# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci)
#else
# ifdef GC_ADD_CALLER
# define ADD_CALL_CHAIN(base, ra) ((oh *)(base)) -> oh_ci[0].ci_pc = (ra)
# define PRINT_CALL_CHAIN(base) GC_print_callers(((oh *)(base)) -> oh_ci)
# else
# define ADD_CALL_CHAIN(base, ra)
# define PRINT_CALL_CHAIN(base)
# endif
#endif
# ifdef GC_ADD_CALLER
# define OPT_RA ra,
# else
# define OPT_RA
# endif
/* Check whether object with base pointer p has debugging info */
/* p is assumed to point to a legitimate object in our part */
/* of the heap. */
#ifdef SHORT_DBG_HDRS
# define GC_has_other_debug_info(p) TRUE
#else
GC_bool GC_has_other_debug_info(/* p */);
#endif
#ifdef KEEP_BACK_PTRS
# define GC_HAS_DEBUG_INFO(p) \
((((oh *)p)->oh_back_ptr & 1) && GC_has_other_debug_info(p))
#else
# define GC_HAS_DEBUG_INFO(p) GC_has_other_debug_info(p)
#endif
/* Store debugging info into p. Return displaced pointer. */
/* Assumes we don't hold allocation lock. */
ptr_t GC_store_debug_info(/* p, sz, string, integer */);
#endif /* _DBG_MLC_H */
#ifdef SOLARIS_THREADS
/* The set of all known threads. We intercept thread creation and */
/* joins. We never actually create detached threads. We allocate all */
/* new thread stacks ourselves. These allow us to maintain this */
/* data structure. */
/* Protected by GC_thr_lock. */
/* Some of this should be declared volatile, but that's incosnsistent */
/* with some library routine declarations. In particular, the */
/* definition of cond_t doesn't mention volatile! */
typedef struct GC_Thread_Rep {
struct GC_Thread_Rep * next;
thread_t id;
word flags;
# define FINISHED 1 /* Thread has exited. */
# define DETACHED 2 /* Thread is intended to be detached. */
# define CLIENT_OWNS_STACK 4
/* Stack was supplied by client. */
# define SUSPENDED 8 /* Currently suspended. */
ptr_t stack;
size_t stack_size;
cond_t join_cv;
void * status;
} * GC_thread;
extern GC_thread GC_new_thread(thread_t id);
extern GC_bool GC_thr_initialized;
extern volatile GC_thread GC_threads[];
extern size_t GC_min_stack_sz;
extern size_t GC_page_sz;
extern void GC_thr_init(void);
# endif /* SOLARIS_THREADS */
/*
* This is a reimplementation of a subset of the pthread_getspecific/setspecific
* interface. This appears to outperform the standard linuxthreads one
* by a significant margin.
* The major restriction is that each thread may only make a single
* pthread_setspecific call on a single key. (The current data structure
* doesn't really require that. The restriction should be easily removable.)
* We don't currently support the destruction functions, though that
* could be done.
* We also currently assume that only one pthread_setspecific call
* can be executed at a time, though that assumption would be easy to remove
* by adding a lock.
*/
#include <errno.h>
/* Called during key creation or setspecific. */
/* For the GC we already hold lock. */
/* Currently allocated objects leak on thread exit. */
/* That's hard to fix, but OK if we allocate garbage */
/* collected memory. */
#define MALLOC_CLEAR(n) GC_INTERNAL_MALLOC(n, NORMAL)
#define PREFIXED(name) GC_##name
#define TS_CACHE_SIZE 1024
#define CACHE_HASH(n) (((((long)n) >> 8) ^ (long)n) & (TS_CACHE_SIZE - 1))
#define TS_HASH_SIZE 1024
#define HASH(n) (((((long)n) >> 8) ^ (long)n) & (TS_HASH_SIZE - 1))
typedef struct thread_specific_entry {
unsigned long qtid; /* quick thread id, only for cache */
void * value;
pthread_t thread;
struct thread_specific_entry *next;
} tse;
/* We represent each thread-specific datum as two tables. The first is */
/* a cache, index by a "quick thread identifier". The "quick" thread */
/* identifier is an easy to compute value, which is guaranteed to */
/* determine the thread, though a thread may correspond to more than */
/* one value. We typically use the address of a page in the stack. */
/* The second is a hash table, indexed by pthread_self(). It is used */
/* only as a backup. */
/* Return the "quick thread id". Default version. Assumes page size, */
/* or at least thread stack separation, is at least 4K. */
static __inline__ long quick_thread_id() {
int dummy;
return (long)(&dummy) >> 12;
}
#define INVALID_QTID ((unsigned long)(-1))
typedef struct thread_specific_data {
tse * volatile cache[TS_CACHE_SIZE];
/* A faster index to the hash table */
tse * hash[TS_HASH_SIZE];
pthread_mutex_t lock;
} tsd;
typedef tsd * PREFIXED(key_t);
extern int PREFIXED(key_create) (tsd ** key_ptr, void (* destructor)(void *));
extern int PREFIXED(setspecific) (tsd * key, void * value);
extern void PREFIXED(remove_specific) (tsd * key);
/* An internal version of getspecific that assumes a cache miss. */
void * PREFIXED(slow_getspecific) (tsd * key, unsigned long qtid,
tse * volatile * cache_entry);
static __inline__ void * PREFIXED(getspecific) (tsd * key) {
long qtid = quick_thread_id();
unsigned hash_val = CACHE_HASH(qtid);
tse * volatile * entry_ptr = key -> cache + hash_val;
tse * entry = *entry_ptr; /* Must be loaded only once. */
if (entry -> qtid == qtid) return entry -> value;
return PREFIXED(slow_getspecific) (key, qtid, entry_ptr);
}
.text
.set linkageArea,24
.set params,4
.set alignment,4
.set spaceToSave,linkageArea+params+alignment
.set spaceToSave8,spaceToSave+8
; Mark from machine registers that are saved by C compiler
.globl _GC_push_regs
_GC_push_regs:
; PROLOG
mflr r0 ; get return address
stw r0,8(r1) ; save return address
stwu r1,-spaceToSave(r1) ; skip over caller save area
;
mr r3,r2 ; mark from r2. Well I'm not really sure
; that this is necessary or even the right
; thing to do - at least it doesn't harm...
; According to Apple's docs it points to
; the direct data area, whatever that is...
bl _GC_push_one
mr r3,r13 ; mark from r13-r31
bl _GC_push_one
mr r3,r14
bl _GC_push_one
mr r3,r15
bl _GC_push_one
mr r3,r16
bl _GC_push_one
mr r3,r17
bl _GC_push_one
mr r3,r18
bl _GC_push_one
mr r3,r19
bl _GC_push_one
mr r3,r20
bl _GC_push_one
mr r3,r21
bl _GC_push_one
mr r3,r22
bl _GC_push_one
mr r3,r23
bl _GC_push_one
mr r3,r24
bl _GC_push_one
mr r3,r25
bl _GC_push_one
mr r3,r26
bl _GC_push_one
mr r3,r27
bl _GC_push_one
mr r3,r28
bl _GC_push_one
mr r3,r29
bl _GC_push_one
mr r3,r30
bl _GC_push_one
mr r3,r31
bl _GC_push_one
; EPILOG
lwz r0,spaceToSave8(r1) ; get return address back
mtlr r0 ; reset link register
addic r1,r1,spaceToSave ; restore stack pointer
blr
! SPARCompiler 3.0 and later apparently no longer handles
! asm outside functions. So we need a separate .s file
! This is only set up for SunOS 4.
! Assumes this is called before the stack contents are
! examined.
#include "machine/asm.h"
.seg "text"
.globl _C_LABEL(GC_save_regs_in_stack)
.globl _C_LABEL(GC_push_regs)
_C_LABEL(GC_save_regs_in_stack):
_C_LABEL(GC_push_regs):
ta 0x3 ! ST_FLUSH_WINDOWS
mov %sp,%o0
retl
nop
.globl _C_LABEL(GC_clear_stack_inner)
_C_LABEL(GC_clear_stack_inner):
mov %sp,%o2 ! Save sp
add %sp,-8,%o3 ! p = sp-8
clr %g1 ! [g0,g1] = 0
add %o1,-0x60,%sp ! Move sp out of the way,
! so that traps still work.
! Includes some extra words
! so we can be sloppy below.
loop:
std %g0,[%o3] ! *(long long *)p = 0
cmp %o3,%o1
bgu loop ! if (p > limit) goto loop
add %o3,-8,%o3 ! p -= 8 (delay slot)
retl
mov %o2,%sp ! Restore sp., delay slot
/*
* Copyright (c) 2000 by Hewlett-Packard Company. All rights reserved.
*
* THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
* OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
*
* Permission is hereby granted to use or copy this program
* for any purpose, provided the above notices are retained on all copies.
* Permission to modify the code and to distribute modified code is granted,
* provided the above notices are retained, and a notice that the code was
* modified is included with the above copyright notice.
*/
#if defined(LINUX_THREADS) || defined(GC_LINUX_THREADS)
#include "private/gc_priv.h" /* For GC_compare_and_exchange, GC_memory_barrier */
#include "private/specific.h"
static tse invalid_tse; /* 0 qtid is guaranteed to be invalid */
int PREFIXED(key_create) (tsd ** key_ptr, void (* destructor)(void *)) {
int i;
tsd * result = (tsd *)MALLOC_CLEAR(sizeof (tsd));
if (0 == result) return ENOMEM;
pthread_mutex_init(&(result -> lock), NULL);
for (i = 0; i < TS_CACHE_SIZE; ++i) {
result -> cache[i] = &invalid_tse;
}
*key_ptr = result;
return 0;
}
int PREFIXED(setspecific) (tsd * key, void * value) {
pthread_t self = pthread_self();
int hash_val = HASH(self);
volatile tse * entry = (volatile tse *)MALLOC_CLEAR(sizeof (tse));
if (0 == entry) return ENOMEM;
pthread_mutex_lock(&(key -> lock));
/* Could easily check for an existing entry here. */
entry -> next = key -> hash[hash_val];
entry -> thread = self;
entry -> value = value;
/* There can only be one writer at a time, but this needs to be */
/* atomic with respect to concurrent readers. */
*(volatile tse **)(key -> hash + hash_val) = entry;
pthread_mutex_unlock(&(key -> lock));
return 0;
}
/* Remove thread-specific data for this thread. Should be called on */
/* thread exit. */
void PREFIXED(remove_specific) (tsd * key) {
pthread_t self = pthread_self();
unsigned hash_val = HASH(self);
tse *entry;
tse **link = key -> hash + hash_val;
pthread_mutex_lock(&(key -> lock));
entry = *link;
while (entry != NULL && entry -> thread != self) {
link = &(entry -> next);
entry = *link;
}
/* Invalidate qtid field, since qtids may be reused, and a later */
/* cache lookup could otherwise find this entry. */
entry -> qtid = INVALID_QTID;
if (entry != NULL) {
*link = entry -> next;
/* Atomic! concurrent accesses still work. */
/* They must, since readers don't lock. */
}
/* If we wanted to deallocate the entry, we'd first have to clear */
/* any cache entries pointing to it. That probably requires */
/* additional synchronization, since we can't prevent a concurrent */
/* cache lookup, which should still be examining deallocated memory.*/
/* This can only happen if the concurrent access is from another */
/* thread, and hence has missed the cache, but still... */
/* With GC, we're done, since the pointers from the cache will */
/* be overwritten, all local pointers to the entries will be */
/* dropped, and the entry will then be reclaimed. */
pthread_mutex_unlock(&(key -> lock));
}
/* Note that even the slow path doesn't lock. */
void * PREFIXED(slow_getspecific) (tsd * key, unsigned long qtid,
tse * volatile * cache_ptr) {
pthread_t self = pthread_self();
unsigned hash_val = HASH(self);
tse *entry = key -> hash[hash_val];
while (entry != NULL && entry -> thread != self) {
entry = entry -> next;
}
if (entry == NULL) return NULL;
/* Set cache_entry. */
entry -> qtid = qtid;
/* It's safe to do this asynchronously. Either value */
/* is safe, though may produce spurious misses. */
*cache_ptr = entry;
/* Again this is safe since pointer assignments are */
/* presumed atomic, and either pointer is valid. */
return entry -> value;
}
#endif /* LINUX_THREADS */
#include "leak_detector.h"
main() {
int *p[10];
int i;
GC_find_leak = 1; /* for new collect versions not compiled */
/* with -DFIND_LEAK. */
for (i = 0; i < 10; ++i) {
p[i] = malloc(sizeof(int)+i);
}
CHECK_LEAKS();
for (i = 1; i < 10; ++i) {
free(p[i]);
}
for (i = 0; i < 9; ++i) {
p[i] = malloc(sizeof(int)+i);
}
CHECK_LEAKS();
CHECK_LEAKS();
CHECK_LEAKS();
}
/****************************************************************************
Copyright (c) 1994 by Xerox Corporation. All rights reserved.
THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
OR IMPLIED. ANY USE IS AT YOUR OWN RISK.
Permission is hereby granted to use or copy this program for any
purpose, provided the above notices are retained on all copies.
Permission to modify the code and to distribute modified code is
granted, provided the above notices are retained, and a notice that
the code was modified is included with the above copyright notice.
****************************************************************************
Last modified on Mon Jul 10 21:06:03 PDT 1995 by ellis
modified on December 20, 1994 7:27 pm PST by boehm
usage: test_cpp number-of-iterations
This program tries to test the specific C++ functionality provided by
gc_c++.h that isn't tested by the more general test routines of the
collector.
A recommended value for number-of-iterations is 10, which will take a
few minutes to complete.
***************************************************************************/
#include "gc_cpp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __GNUC__
# include "new_gc_alloc.h"
#else
# include "gc_alloc.h"
#endif
extern "C" {
#include "private/gc_priv.h"
}
#ifdef MSWIN32
# include <windows.h>
#endif
#ifdef GC_NAME_CONFLICT
# define USE_GC UseGC
struct foo * GC;
#else
# define USE_GC GC
#endif
#define my_assert( e ) \
if (! (e)) { \
GC_printf1( "Assertion failure in " __FILE__ ", line %d: " #e "\n", \
__LINE__ ); \
exit( 1 ); }
class A {public:
/* An uncollectable class. */
A( int iArg ): i( iArg ) {}
void Test( int iArg ) {
my_assert( i == iArg );}
int i;};
class B: public gc, public A {public:
/* A collectable class. */
B( int j ): A( j ) {}
~B() {
my_assert( deleting );}
static void Deleting( int on ) {
deleting = on;}
static int deleting;};
int B::deleting = 0;
class C: public gc_cleanup, public A {public:
/* A collectable class with cleanup and virtual multiple inheritance. */
C( int levelArg ): A( levelArg ), level( levelArg ) {
nAllocated++;
if (level > 0) {
left = new C( level - 1 );
right = new C( level - 1 );}
else {
left = right = 0;}}
~C() {
this->A::Test( level );
nFreed++;
my_assert( level == 0 ?
left == 0 && right == 0 :
level == left->level + 1 && level == right->level + 1 );
left = right = 0;
level = -123456;}
static void Test() {
my_assert( nFreed <= nAllocated && nFreed >= .8 * nAllocated );}
static int nFreed;
static int nAllocated;
int level;
C* left;
C* right;};
int C::nFreed = 0;
int C::nAllocated = 0;
class D: public gc {public:
/* A collectable class with a static member function to be used as
an explicit clean-up function supplied to ::new. */
D( int iArg ): i( iArg ) {
nAllocated++;}
static void CleanUp( void* obj, void* data ) {
D* self = (D*) obj;
nFreed++;
my_assert( self->i == (int) (long) data );}
static void Test() {
my_assert( nFreed >= .8 * nAllocated );}
int i;
static int nFreed;
static int nAllocated;};
int D::nFreed = 0;
int D::nAllocated = 0;
class E: public gc_cleanup {public:
/* A collectable class with clean-up for use by F. */
E() {
nAllocated++;}
~E() {
nFreed++;}
static int nFreed;
static int nAllocated;};
int E::nFreed = 0;
int E::nAllocated = 0;
class F: public E {public:
/* A collectable class with clean-up, a base with clean-up, and a
member with clean-up. */
F() {
nAllocated++;}
~F() {
nFreed++;}
static void Test() {
my_assert( nFreed >= .8 * nAllocated );
my_assert( 2 * nFreed == E::nFreed );}
E e;
static int nFreed;
static int nAllocated;};
int F::nFreed = 0;
int F::nAllocated = 0;
long Disguise( void* p ) {
return ~ (long) p;}
void* Undisguise( long i ) {
return (void*) ~ i;}
#ifdef MSWIN32
int APIENTRY WinMain(
HINSTANCE instance, HINSTANCE prev, LPSTR cmd, int cmdShow )
{
int argc;
char* argv[ 3 ];
for (argc = 1; argc < sizeof( argv ) / sizeof( argv[ 0 ] ); argc++) {
argv[ argc ] = strtok( argc == 1 ? cmd : 0, " \t" );
if (0 == argv[ argc ]) break;}
#else
# ifdef MACOS
int main() {
# else
int main( int argc, char* argv[] ) {
# endif
#endif
# if defined(MACOS) // MacOS
char* argv_[] = {"test_cpp", "10"}; // doesn't
argv = argv_; // have a
argc = sizeof(argv_)/sizeof(argv_[0]); // commandline
# endif
int i, iters, n;
# if !defined(MACOS)
# ifdef __GNUC__
int *x = (int *)gc_alloc::allocate(sizeof(int));
# else
int *x = (int *)alloc::allocate(sizeof(int));
# endif
*x = 29;
x -= 3;
# endif
if (argc != 2 || (0 >= (n = atoi( argv[ 1 ] )))) {
GC_printf0( "usage: test_cpp number-of-iterations\n" );
exit( 1 );}
for (iters = 1; iters <= n; iters++) {
GC_printf1( "Starting iteration %d\n", iters );
/* Allocate some uncollectable As and disguise their pointers.
Later we'll check to see if the objects are still there. We're
checking to make sure these objects really are uncollectable. */
long as[ 1000 ];
long bs[ 1000 ];
for (i = 0; i < 1000; i++) {
as[ i ] = Disguise( new (NoGC) A( i ) );
bs[ i ] = Disguise( new (NoGC) B( i ) );}
/* Allocate a fair number of finalizable Cs, Ds, and Fs.
Later we'll check to make sure they've gone away. */
for (i = 0; i < 1000; i++) {
C* c = new C( 2 );
C c1( 2 ); /* stack allocation should work too */
D* d = ::new (USE_GC, D::CleanUp, (void*)(long)i) D( i );
F* f = new F;
if (0 == i % 10) delete c;}
/* Allocate a very large number of collectable As and Bs and
drop the references to them immediately, forcing many
collections. */
for (i = 0; i < 1000000; i++) {
A* a = new (USE_GC) A( i );
B* b = new B( i );
b = new (USE_GC) B( i );
if (0 == i % 10) {
B::Deleting( 1 );
delete b;
B::Deleting( 0 );}
# ifdef FINALIZE_ON_DEMAND
GC_invoke_finalizers();
# endif
}
/* Make sure the uncollectable As and Bs are still there. */
for (i = 0; i < 1000; i++) {
A* a = (A*) Undisguise( as[ i ] );
B* b = (B*) Undisguise( bs[ i ] );
a->Test( i );
delete a;
b->Test( i );
B::Deleting( 1 );
delete b;
B::Deleting( 0 );
# ifdef FINALIZE_ON_DEMAND
GC_invoke_finalizers();
# endif
}
/* Make sure most of the finalizable Cs, Ds, and Fs have
gone away. */
C::Test();
D::Test();
F::Test();}
# if !defined(__GNUC__) && !defined(MACOS)
my_assert (29 == x[3]);
# endif
GC_printf0( "The test appears to have succeeded.\n" );
return( 0 );}
#define GC_LINUX_THREADS
#include "leak_detector.h"
#include <pthread.h>
#include <stdio.h>
void * test(void * arg) {
int *p[10];
int i;
GC_find_leak = 1; /* for new collect versions not compiled */
/* with -DFIND_LEAK. */
for (i = 0; i < 10; ++i) {
p[i] = malloc(sizeof(int)+i);
}
CHECK_LEAKS();
for (i = 1; i < 10; ++i) {
free(p[i]);
}
}
#define NTHREADS 5
main() {
int i;
pthread_t t[NTHREADS];
int code;
for (i = 0; i < NTHREADS; ++i) {
if ((code = pthread_create(t + i, 0, test, 0)) != 0) {
printf("Thread creation failed %d\n", code);
}
}
for (i = 0; i < NTHREADS; ++i) {
if ((code = pthread_join(t[i], 0)) != 0) {
printf("Thread join failed %lu\n", code);
}
}
CHECK_LEAKS();
CHECK_LEAKS();
CHECK_LEAKS();
}
#include <stdio.h>
#define GC_DEBUG
#include "gc.h"
struct treenode {
struct treenode *x;
struct treenode *y;
} * root[10];
struct treenode * mktree(int i) {
struct treenode * r = GC_MALLOC(sizeof(struct treenode));
if (0 == i) return 0;
r -> x = mktree(i-1);
r -> y = mktree(i-1);
return r;
}
main()
{
int i;
for (i = 0; i < 10; ++i) {
root[i] = mktree(12);
}
GC_generate_random_backtrace();
GC_generate_random_backtrace();
GC_generate_random_backtrace();
GC_generate_random_backtrace();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment