reftrack is a GCC plugin for C language that tracks references to allocated objects though it could be used for other purposes by writing custom functions.
- GCC: >= 12.3 Other GCC versions might work, but testing on 9.3 showed that GCC encounters an internal compiler error at -O2 or -O3 on some test cases.
- make: >= 4.2.1
Unpack the source package and run make install
to install the gcc
plugin. You could pass a value for DESTDIR if you need to package it.
$ cd testcases
$ make run
All test cases should pass.
Add
-fplugin=/usr/lib64/reftrack.so -I/usr/include/reftrack -fplugin-arg-reftrack-addref=reftrack_addref -fplugin-arg-reftrack-removeref=reftrack_removeref
to CFLAGS
.
These are the minimum arguments required.
TODO
First step is to tag the structure that needs to be tracked.
struct S;
static void S_addref(const struct S *const);
static void S_removeref(const struct S *const);
struct __attribute__((__reftrack__(S_addref, S_removeref))) S {
int foo;
char bar;
};
The functions S_addref
, S_removeref
could be called anything as long
as their signatures are exactly same as in the example above.
It becomes tedious to write these declarations for every structure that
needs to be tracked, hence the macro REFTRACK_STRUCT
has been provided
which simplifies the declarations to a form as given below.
REFTRACK_STRUCT(S) {
int foo;
char bar;
};
REFTRACK_EPILOG(S)
REFTRACK_EPILOG(S)
is a macro that provides default definitions for
S_addref()
and S_removeref()
. If you want to call a destructor
before the object is released, use REFTRACK_EPILOG_WITH_DTOR(S)
macro.
The plugin transforms the original C code wherever it encounters statements involving pointers to structures that have the reftrack attribute applied to them.
typedef struct S S;
S *p = ... , *q = ...;
p = q;
is transformed into
typedef struct S S;
S *p = ... , *q = ...;
S_addref(q);
S_removeref(p);
p = q;
S *p = ...;
void print_S(S *sp){
stat1;
stat2;
}
print_S(p);
is transformed into
S *p = ...;
void print_S(S *sp){
stat1;
stat2;
S_removeref(sp);
}
S_addref(p);
print_S(p);
S *p = ...;
S *get_obj(int);
p = get_obj(123);
is transformed into
S *p = ...;
S *get_obj(int);
S_removeref(p);
p = get_obj(123);
S_addref(p);
One of the motivation behind the development of this plugin is to implement garbage collection for the C programming language that is natively supported by the compiler. The addref, removeref instrumentation makes it easier to implement reference counted GC rather than mark and sweep GC and a sample implementation is provided in hrcmm.h.
The rc_malloc()
, rc_calloc()
are wrappers for the standard
malloc()
, calloc()
functions that prefix a small header to the
object allocation by requesting extra memory for the header. The header
contains the reference count and optionally the filename and line number
where allocation took place. Similarly, rc_realloc()
, rc_free()
are wrappers for realloc()
and free()
.
Note: There is no need to call rc_free()
directly in almost all
cases.
REFTRACK_STRUCT(X)
Declares functionsX_addref()
,X_removeref()
REFTRACK_EPILOG(X)
Defines functionsX_create()
,X_addref()
, andX_removeref()
REFTRACK_EPILOG_WITH_DTOR(X)
Same asREFTRACK_EPILOG(X)
, but callsX_destroy()
before callingfree()
. The programmer has to provide a definition forX_destroy()
.REFTRACK_DEBUG
Prints the location of allocation and release of memory objects. Uses extra space in the allocated object.REFTRACK_COUNT(p)
Returns the reference count of the given pointer.
Destructors are special functions that get called on an object when
their reference count is zero and is about to be freed. They must have
the signature REFTRACK_DESTRUCTOR_FN void X_destroy(struct X *const)
Functions that change the attributes or size of an allocated memory like
realloc()
should be tagged with REFTRACK_HEAP_FN
. This attribute
is useful only if you are implementing a custom GC solution. In all
other cases, it might not be necessary.
TODO The header file requires more testing.
- Array of tracked pointers is currently unsupported. Look at array2.c
example in the
testcases
directory for a way to handle them. - The plugin is unable to distinguish pointers holding an address to object on the stack vs heap. Use of a mark in the header attached to the allocated object mitigates it in most cases at the cost of extra storage.