gc: add `-gc boehm_leak` for leak detection (#9464)

pull/9468/head
Uwe Krüger 2021-03-25 16:52:33 +01:00 committed by GitHub
parent 03d56865e3
commit 257eadd2e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 87 additions and 15 deletions

View File

@ -140,6 +140,21 @@ jobs:
- name: Self tests with `-gc boehm`
## The test cases are run with non-gc `v` for now
run: ./v -gc boehm -silent test-self
- name: Test leak detector
run: |
./v -gc boehm_leak -o testcase_leak vlib/v/tests/testcase_leak.v
./testcase_leak 2>leaks.txt
grep "Found 1 leaked object" leaks.txt && grep ", sz=1000," leaks.txt
- name: Test leak detector not being active for `-gc boehm`
run: |
./v -gc boehm -o testcase_leak vlib/v/tests/testcase_leak.v
./testcase_leak 2>leaks.txt
[ "$(stat -c %s leaks.txt)" = "0" ]
- name: Test leak detector not being active for normal compile
run: |
./v -o testcase_leak vlib/v/tests/testcase_leak.v
./testcase_leak 2>leaks.txt
[ "$(stat -c %s leaks.txt)" = "0" ]
misc-tooling:
runs-on: ubuntu-20.04

View File

@ -72,11 +72,12 @@ see also `v help build`.
-gc <mode>
Use and link an optional garbage collector.
Only `-gc boehm` is supported currently. You need to install a
`libgc-dev` package first, or install it manually from source:
Only `-gc boehm` and `-gc boehm_leak` are supported currently.
You need to install a `libgc-dev` package first, or install it manually
from source:
https://github.com/ivmai/bdwgc
Note, this option is complementary to -autofree. The Boehm garbage
Note, `-gc boehm` is complementary to -autofree. The Boehm garbage
collector is conservative, and it may make your program significantly
slower if it does many small allocations in a loop. This option
is intended *mainly* for reducing the memory usage of programs, that
@ -84,6 +85,11 @@ see also `v help build`.
environments like small VPSes, and for which a few ms of garbage
collection pauses from time to time *do not matter much*.
The option `-gc boehm_leak` is intended for leak detection in
manual memory management. The function `gc_check_leaks()`
can be called to get detection results. This function is a no-op
when `-gc boehm_leak` is not supplied.
# Miscellaneous:
-printfn <fn_name>
Print the content of the generated C function named fn_name.

View File

@ -315,9 +315,14 @@ pub fn free(ptr voidptr) {
return
}
$if gcboehm ? {
// It is better to leave it to Boehm's gc to free things.
// It is generally better to leave it to Boehm's gc to free things.
// Calling C.GC_FREE(ptr) was tried initially, but does not work
// well with programs that do manual management themselves.
//
// The exception is doing leak detection for manual memory management:
$if gcboehm_leak ? {
C.GC_FREE(ptr)
}
return
}
C.free(ptr)

View File

@ -9,18 +9,38 @@ $if windows {
$if macos {
#pkgconfig bdw-gc
}
$if gcboehm_leak ? {
#define GC_DEBUG
}
#include <gc.h>
#flag -lgc
// replacements for `malloc()/calloc()`, `realloc()` and `free()`
// for use with Boehm-GC
// Do not use them manually. They are automatically chosen when
// compiled with `-gc boehm` or `-gc boehm_leak`.
fn C.GC_MALLOC(n size_t) voidptr
fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr
fn C.GC_FREE(ptr voidptr)
fn C.GC_set_find_leak(int)
// fn C.CHECK_LEAKS()
// explicitely perform garbage collection now! Garbage collections
// are done automatically when needed, so this function is hardly needed
fn C.GC_gcollect()
// functions to temporarily suspend/resume garbage collection
fn C.GC_disable()
fn C.GC_enable()
// returns non-zero if GC is disabled
fn C.GC_is_disabled() int
// for leak detection it is advisable to do explicit garbage collections
pub fn gc_check_leaks() {
$if gcboehm_leak ? {
C.GC_gcollect()
}
}

View File

@ -10,5 +10,7 @@ fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr
fn C.GC_FREE(ptr voidptr)
// fn C.CHECK_LEAKS()
fn C.GC_gcollect()
// provide an empty function when manual memory management is used
// to simplify leak detection
//
pub fn gc_check_leaks() {}

View File

@ -423,7 +423,7 @@ pub fn (mut g Gen) init() {
}
g.comptime_defines.writeln('')
}
if g.pref.gc_mode == .boehm {
if g.pref.gc_mode in [.boehm, .boehm_leak] {
g.comptime_defines.writeln('#define _VGCBOEHM (1)')
}
if g.pref.is_debug || 'debug' in g.pref.compile_defines {

View File

@ -67,8 +67,11 @@ fn (mut g Gen) gen_c_main_function_header() {
fn (mut g Gen) gen_c_main_header() {
g.gen_c_main_function_header()
if g.pref.gc_mode == .boehm {
if g.pref.gc_mode in [.boehm, .boehm_leak] {
g.writeln('#if defined(_VGCBOEHM)')
if g.pref.gc_mode == .boehm_leak {
g.writeln('\tGC_set_find_leak(1);')
}
g.writeln('\tGC_INIT();')
g.writeln('#endif')
}
@ -155,8 +158,11 @@ pub fn (mut g Gen) gen_c_main_for_tests() {
main_fn_start_pos := g.out.len
g.writeln('')
g.gen_c_main_function_header()
if g.pref.gc_mode == .boehm {
if g.pref.gc_mode in [.boehm, .boehm_leak] {
g.writeln('#if defined(_VGCBOEHM)')
if g.pref.gc_mode == .boehm_leak {
g.writeln('\tGC_set_find_leak(1);')
}
g.writeln('\tGC_INIT();')
g.writeln('#endif')
}

View File

@ -20,6 +20,7 @@ pub enum BuildMode {
pub enum GarbageCollectionMode {
no_gc
boehm
boehm_leak
}
pub enum OutputMode {
@ -158,7 +159,7 @@ pub mut:
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
cache_manager vcache.CacheManager
is_help bool // -h, -help or --help was passed
gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm
gc_mode GarbageCollectionMode = .no_gc // .no_gc, .boehm, .boehm_leak
// checker settings:
checker_match_exhaustive_cutoff_limit int = 10
}
@ -230,8 +231,13 @@ pub fn parse_args(known_external_commands []string, args []string) (&Preferences
res.gc_mode = .boehm
parse_define(mut res, 'gcboehm')
}
'boehm_leak' {
res.gc_mode = .boehm_leak
parse_define(mut res, 'gcboehm')
parse_define(mut res, 'gcboehm_leak')
}
else {
eprintln('unknown garbage collection mode, only `-gc boehm` is supported')
eprintln('unknown garbage collection mode, only `-gc boehm` and `-gc boehm_leak` are supported')
exit(1)
}
}

View File

@ -0,0 +1,12 @@
// This program is supposed to test the leak detector which
// is integrated with `-gc boehm_leak` at compile time.
//
// If compiled with `-gc boehm` or without `-gc` nothing
// will be reported.
fn main() {
mut y := unsafe { malloc(1000) }
// unsafe { free(y) } // leak if commented out
y = voidptr(0)
gc_check_leaks()
}