aboutsummaryrefslogtreecommitdiff
path: root/core/sys/valgrind/memcheck.odin
blob: bc77444be9babe5c0fbbc4cf6cae088796e5fd03 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#+build amd64
package sys_valgrind

import "base:intrinsics"

Mem_Check_Client_Request :: enum uintptr {
	Make_Mem_No_Access = 'M'<<24 | 'C'<<16,
	Make_Mem_Undefined,
	Make_Mem_Defined,
	Discard,
	Check_Mem_Is_Addressable,
	Check_Mem_Is_Defined,
	Do_Leak_Check,
	Count_Leaks,
	Get_Vbits,
	Set_Vbits,
	Create_Block,
	Make_Mem_Defined_If_Addressable,
	Count_Leak_Blocks,
	Enable_Addr_Error_Reporting_In_Range,
	Disable_Addr_Error_Reporting_In_Range,
}

@(require_results)
mem_check_client_request_expr :: #force_inline proc "c" (default: uintptr, request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
	return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
}
mem_check_client_request_stmt :: #force_inline proc "c" (request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) {
	_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
}

// Mark memory at `raw_data(qzz)` as unaddressable for `len(qzz)` bytes.
// Returns true when run on Valgrind and false otherwise.
make_mem_no_access :: proc "c" (qzz: []byte) -> bool {
	return 0 != mem_check_client_request_expr(0, .Make_Mem_No_Access, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Mark memory at `raw_data(qzz)` as addressable but undefined for `len(qzz)` bytes.
// Returns true when run on Valgrind and false otherwise.
make_mem_undefined :: proc "c" (qzz: []byte) -> bool {
	return 0 != mem_check_client_request_expr(0, .Make_Mem_Undefined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Mark memory at `raw_data(qzz)` as addressable for `len(qzz)` bytes.
// Returns true when run on Valgrind and false otherwise.
make_mem_defined :: proc "c" (qzz: []byte) -> bool {
	return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}

// Check that memory at `raw_data(qzz)` is addressable for `len(qzz)` bytes.
// If suitable addressibility is not established, Valgrind prints an error
// message and returns the address of the first offending byte.
// Otherwise it returns zero.
check_mem_is_addressable :: proc "c" (qzz: []byte) -> uintptr {
	return mem_check_client_request_expr(0, .Check_Mem_Is_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Check that memory at `raw_data(qzz)` is addressable and defined for `len(qzz)` bytes.
// If suitable addressibility and definedness are not established,
// Valgrind prints an error message and returns the address of the first
// offending byte. Otherwise it returns zero.
check_mem_is_defined :: proc "c" (qzz: []byte) -> uintptr {
	return mem_check_client_request_expr(0, .Check_Mem_Is_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}

// Similar to `make_mem_defined(qzz)` except that addressability is not altered:
// bytes which are addressable are marked as defined, but those which
// are not addressable are left unchanged.
// Returns true when run on Valgrind and false otherwise.
make_mem_defined_if_addressable :: proc "c" (qzz: []byte) -> bool {
	return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined_If_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}

// Create a block-description handle.
// The description is an ascii string which is included in any messages
// pertaining to addresses within the specified memory range.
// Has no other effect on the properties of the memory range.
create_block :: proc "c" (qzz: []u8, desc: cstring) -> bool {
	return 0 != mem_check_client_request_expr(0, .Create_Block, uintptr(raw_data(qzz)), uintptr(len(qzz)), uintptr(rawptr(desc)), 0, 0)
}
// Discard a block-description-handle. Returns true for an invalid handle, false for a valid handle.
discard :: proc "c" (blk_index: uintptr) -> bool {
	return 0 != mem_check_client_request_expr(0, .Discard, 0, blk_index, 0, 0, 0)
}


// Do a full memory leak check (like `--leak-check=full`) mid-execution.
leak_check :: proc "c" () {
	mem_check_client_request_stmt(.Do_Leak_Check, 0, 0, 0, 0, 0)
}
// Same as `leak_check()` but only showing the entries for which there was an increase
// in leaked bytes or leaked nr of blocks since the previous leak search.
added_leak_check :: proc "c" () {
	mem_check_client_request_stmt(.Do_Leak_Check, 0, 1, 0, 0, 0)
}
// Same as `added_leak_check()` but showing entries with increased or decreased
// leaked bytes/blocks since previous leak search.
changed_leak_check :: proc "c" () {
	mem_check_client_request_stmt(.Do_Leak_Check, 0, 2, 0, 0, 0)
}
// Do a summary memory leak check (like `--leak-check=summary`) mid-execution.
quick_leak_check :: proc "c" () {
	mem_check_client_request_stmt(.Do_Leak_Check, 1, 0, 0, 0, 0)
}

Count_Result :: struct {
	leaked:     uint,
	dubious:    uint,
	reachable:  uint,
	suppressed: uint,
}

count_leaks :: proc "c" () -> (res: Count_Result) {
	mem_check_client_request_stmt(
		.Count_Leaks,
		uintptr(&res.leaked),
		uintptr(&res.dubious),
		uintptr(&res.reachable),
		uintptr(&res.suppressed),
		0,
	)
	return
}

count_leak_blocks :: proc "c" () -> (res: Count_Result) {
	mem_check_client_request_stmt(
		.Count_Leak_Blocks,
		uintptr(&res.leaked),
		uintptr(&res.dubious),
		uintptr(&res.reachable),
		uintptr(&res.suppressed),
		0,
	)
	return
}

// Get the validity data for addresses zza and copy it
// into the provided zzvbits array.  Return values:
//     0 - if not running on valgrind
//     1 - success
//     2 - [previously indicated unaligned arrays;  these are now allowed]
//     3 - if any parts of zzsrc/zzvbits are not addressable.
// The metadata is not copied in cases 0, 2 or 3 so it should be
// impossible to segfault your system by using this call.
get_vbits :: proc(zza, zzvbits: []byte) -> u8 {
	// assert requires a `context` thus these procedures cannot `proc "c"`
	assert(len(zzvbits) >= len(zza)/8)
	return u8(mem_check_client_request_expr(0, .Get_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0))
}

// Set the validity data for addresses zza, copying it
// from the provided zzvbits array.  Return values:
//     0 - if not running on valgrind
//     1 - success
//     2 - [previously indicated unaligned arrays;  these are now allowed]
//     3 - if any parts of zza/zzvbits are not addressable.
// The metadata is not copied in cases 0, 2 or 3 so it should be
// impossible to segfault your system by using this call.
set_vbits :: proc(zzvbits, zza: []byte) -> u8 {
	// assert requires a `context` thus these procedures cannot `proc "c"`
	assert(len(zzvbits) >= len(zza)/8)
	return u8(mem_check_client_request_expr(0, .Set_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0))
}

// (Re-)enable reporting of addressing errors in the specified address range.
enable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr {
	return mem_check_client_request_expr(0, .Enable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}
// Disable reporting of addressing errors in the specified address range.
disable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr {
	return mem_check_client_request_expr(0, .Disable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
}