mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
gc: add -gc boehm_leak
for leak detection (#9464)
This commit is contained in:
parent
03d56865e3
commit
257eadd2e1
15
.github/workflows/ci.yml
vendored
15
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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() {}
|
||||
|
@ -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 {
|
||||
|
@ -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')
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
12
vlib/v/tests/testcase_leak.v
Normal file
12
vlib/v/tests/testcase_leak.v
Normal 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()
|
||||
}
|
Loading…
Reference in New Issue
Block a user