diff options
| author | Roland Shoemaker <bracewell@google.com> | 2025-03-22 00:58:55 +0000 |
|---|---|---|
| committer | Roland Shoemaker <bracewell@google.com> | 2025-05-21 10:08:08 -0700 |
| commit | 40b19b56a94c4d53a3c1d98275df44049b2f5917 (patch) | |
| tree | f508107b828ff065480c87b2aa2f41c1eceabcc2 /src/syscall/exec_linux.go | |
| parent | 2a5ac1a993efc463efdce7996efd356dabf03a25 (diff) | |
| download | go-40b19b56a94c4d53a3c1d98275df44049b2f5917.tar.xz | |
runtime: add valgrind instrumentation
Add build tag gated Valgrind annotations to the runtime which let it
understand how the runtime manages memory. This allows for Go binaries
to be run under Valgrind without emitting spurious errors.
Instead of adding the Valgrind headers to the tree, and using cgo to
call the various Valgrind client request macros, we just add an assembly
function which emits the necessary instructions to trigger client
requests.
In particular we add instrumentation of the memory allocator, using a
two-level mempool structure (as described in the Valgrind manual [0]).
We also add annotations which allow Valgrind to track which memory we
use for stacks, which seems necessary to let it properly function.
We describe the memory model to Valgrind as follows: we treat heap
arenas as a "pool" created with VALGRIND_CREATE_MEMPOOL_EXT (so that we
can use VALGRIND_MEMPOOL_METAPOOL and VALGRIND_MEMPOOL_AUTO_FREE).
Within the pool we treat spans as "superblocks", annotated with
VALGRIND_MEMPOOL_ALLOC. We then allocate individual objects within spans
with VALGRIND_MALLOCLIKE_BLOCK.
It should be noted that running binaries under Valgrind can be _quite
slow_, and certain operations, such as running the GC, can be _very
slow_. It is recommended to run programs with GOGC=off. Additionally,
async preemption should be turned off, since it'll cause strange
behavior (GODEBUG=asyncpreemptoff=1).
Running Valgrind with --leak-check=yes will result in some errors
resulting from some things not being marked fully free'd. These likely
need more annotations to rectify, but for now it is recommended to run
with --leak-check=off.
Updates #73602
[0] https://valgrind.org/docs/manual/mc-manual.html#mc-manual.mempools
Change-Id: I71b26c47d7084de71ef1e03947ef6b1cc6d38301
Reviewed-on: https://go-review.googlesource.com/c/go/+/674077
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Diffstat (limited to 'src/syscall/exec_linux.go')
| -rw-r--r-- | src/syscall/exec_linux.go | 27 |
1 files changed, 26 insertions, 1 deletions
diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go index 678bc84796..abae9d14eb 100644 --- a/src/syscall/exec_linux.go +++ b/src/syscall/exec_linux.go @@ -800,9 +800,34 @@ func os_checkClonePidfd() error { // pidfd. defer Close(int(pidfd)) + // TODO(roland): this is necessary to prevent valgrind from complaining + // about passing 0x0 to waitid, which is doesn't like. This is clearly not + // ideal. The structures are copied (mostly) verbatim from syscall/unix, + // which we obviously cannot import because of an import loop. + + const is64bit = ^uint(0) >> 63 // 0 for 32-bit hosts, 1 for 64-bit ones. + type sigInfo struct { + Signo int32 + _ struct { + Errno int32 + Code int32 + } // Two int32 fields, swapped on MIPS. + _ [is64bit]int32 // Extra padding for 64-bit hosts only. + + // End of common part. Beginning of signal-specific part. + + Pid int32 + Uid uint32 + Status int32 + + // Pad to 128 bytes. + _ [128 - (6+is64bit)*4]byte + } + for { const _P_PIDFD = 3 - _, _, errno = Syscall6(SYS_WAITID, _P_PIDFD, uintptr(pidfd), 0, WEXITED|WCLONE, 0, 0) + var info sigInfo + _, _, errno = Syscall6(SYS_WAITID, _P_PIDFD, uintptr(pidfd), uintptr(unsafe.Pointer(&info)), WEXITED|WCLONE, 0, 0) if errno != EINTR { break } |
