gc: add `-gc boehm_leak` for leak detection (#9464)
parent
03d56865e3
commit
257eadd2e1
|
@ -140,6 +140,21 @@ jobs:
|
||||||
- name: Self tests with `-gc boehm`
|
- name: Self tests with `-gc boehm`
|
||||||
## The test cases are run with non-gc `v` for now
|
## The test cases are run with non-gc `v` for now
|
||||||
run: ./v -gc boehm -silent test-self
|
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:
|
misc-tooling:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
|
@ -72,11 +72,12 @@ see also `v help build`.
|
||||||
|
|
||||||
-gc <mode>
|
-gc <mode>
|
||||||
Use and link an optional garbage collector.
|
Use and link an optional garbage collector.
|
||||||
Only `-gc boehm` is supported currently. You need to install a
|
Only `-gc boehm` and `-gc boehm_leak` are supported currently.
|
||||||
`libgc-dev` package first, or install it manually from source:
|
You need to install a `libgc-dev` package first, or install it manually
|
||||||
|
from source:
|
||||||
https://github.com/ivmai/bdwgc
|
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
|
collector is conservative, and it may make your program significantly
|
||||||
slower if it does many small allocations in a loop. This option
|
slower if it does many small allocations in a loop. This option
|
||||||
is intended *mainly* for reducing the memory usage of programs, that
|
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
|
environments like small VPSes, and for which a few ms of garbage
|
||||||
collection pauses from time to time *do not matter much*.
|
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:
|
# Miscellaneous:
|
||||||
-printfn <fn_name>
|
-printfn <fn_name>
|
||||||
Print the content of the generated C function named fn_name.
|
Print the content of the generated C function named fn_name.
|
||||||
|
|
|
@ -315,9 +315,14 @@ pub fn free(ptr voidptr) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
$if gcboehm ? {
|
$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
|
// Calling C.GC_FREE(ptr) was tried initially, but does not work
|
||||||
// well with programs that do manual management themselves.
|
// 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
|
return
|
||||||
}
|
}
|
||||||
C.free(ptr)
|
C.free(ptr)
|
||||||
|
|
|
@ -9,18 +9,38 @@ $if windows {
|
||||||
$if macos {
|
$if macos {
|
||||||
#pkgconfig bdw-gc
|
#pkgconfig bdw-gc
|
||||||
}
|
}
|
||||||
|
$if gcboehm_leak ? {
|
||||||
|
#define GC_DEBUG
|
||||||
|
}
|
||||||
#include <gc.h>
|
#include <gc.h>
|
||||||
|
|
||||||
#flag -lgc
|
#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_MALLOC(n size_t) voidptr
|
||||||
|
|
||||||
fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr
|
fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr
|
||||||
|
|
||||||
fn C.GC_FREE(ptr voidptr)
|
fn C.GC_FREE(ptr voidptr)
|
||||||
|
|
||||||
fn C.GC_set_find_leak(int)
|
// explicitely perform garbage collection now! Garbage collections
|
||||||
|
// are done automatically when needed, so this function is hardly needed
|
||||||
// fn C.CHECK_LEAKS()
|
|
||||||
fn C.GC_gcollect()
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,5 +10,7 @@ fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr
|
||||||
|
|
||||||
fn C.GC_FREE(ptr voidptr)
|
fn C.GC_FREE(ptr voidptr)
|
||||||
|
|
||||||
// fn C.CHECK_LEAKS()
|
// provide an empty function when manual memory management is used
|
||||||
fn C.GC_gcollect()
|
// to simplify leak detection
|
||||||
|
//
|
||||||
|
pub fn gc_check_leaks() {}
|
||||||
|
|
|
@ -423,7 +423,7 @@ pub fn (mut g Gen) init() {
|
||||||
}
|
}
|
||||||
g.comptime_defines.writeln('')
|
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)')
|
g.comptime_defines.writeln('#define _VGCBOEHM (1)')
|
||||||
}
|
}
|
||||||
if g.pref.is_debug || 'debug' in g.pref.compile_defines {
|
if g.pref.is_debug || 'debug' in g.pref.compile_defines {
|
||||||
|
|
|
@ -67,8 +67,11 @@ fn (mut g Gen) gen_c_main_function_header() {
|
||||||
|
|
||||||
fn (mut g Gen) gen_c_main_header() {
|
fn (mut g Gen) gen_c_main_header() {
|
||||||
g.gen_c_main_function_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)')
|
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('\tGC_INIT();')
|
||||||
g.writeln('#endif')
|
g.writeln('#endif')
|
||||||
}
|
}
|
||||||
|
@ -155,8 +158,11 @@ pub fn (mut g Gen) gen_c_main_for_tests() {
|
||||||
main_fn_start_pos := g.out.len
|
main_fn_start_pos := g.out.len
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
g.gen_c_main_function_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)')
|
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('\tGC_INIT();')
|
||||||
g.writeln('#endif')
|
g.writeln('#endif')
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ pub enum BuildMode {
|
||||||
pub enum GarbageCollectionMode {
|
pub enum GarbageCollectionMode {
|
||||||
no_gc
|
no_gc
|
||||||
boehm
|
boehm
|
||||||
|
boehm_leak
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum OutputMode {
|
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
|
build_options []string // list of options, that should be passed down to `build-module`, if needed for -usecache
|
||||||
cache_manager vcache.CacheManager
|
cache_manager vcache.CacheManager
|
||||||
is_help bool // -h, -help or --help was passed
|
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 settings:
|
||||||
checker_match_exhaustive_cutoff_limit int = 10
|
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
|
res.gc_mode = .boehm
|
||||||
parse_define(mut res, 'gcboehm')
|
parse_define(mut res, 'gcboehm')
|
||||||
}
|
}
|
||||||
|
'boehm_leak' {
|
||||||
|
res.gc_mode = .boehm_leak
|
||||||
|
parse_define(mut res, 'gcboehm')
|
||||||
|
parse_define(mut res, 'gcboehm_leak')
|
||||||
|
}
|
||||||
else {
|
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)
|
exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
Loading…
Reference in New Issue