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.
- addref default addref function.
- removeref default removeref function.
These two functions should have the signature like void fn(const void *const);
and needs to be specified only for cases where a custom implementation of memory
management is preferred.
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.
Work in progress. Tested against the staging tree.
- 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.