Use BPF to trace linux tracepoints for the write
syscall. This can be used to
watch any write
syscalls a process or a child process makes. This requires
to be run as root.
$ cat t.go
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; ; i++ {
fmt.Printf("%d) hello, go\n", i)
time.Sleep(time.Second)
}
}
$ go run t.go
# Attach to the `go run t.go` process
$ sudo ./bpf-writesnoop 2182
"t" (2260,2182) OUT >> "25) hello, go\n"
"t" (2260,2182) OUT >> "26) hello, go\n"
"t" (2260,2182) OUT >> "27) hello, go\n"
"t" (2260,2182) OUT >> "28) hello, go\n"
"t" (2260,2182) OUT >> "29) hello, go\n"
"t" (2260,2182) OUT >> "30) hello, go\n"
This uses the syscalls:sys_enter_write
tracepoint to watch for any write
syscalls.
Because we only care about what is being written, we can watch the sys_enter tracepoints.
$ sudo cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_write/format
name: sys_enter_write
ID: 648
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:int __syscall_nr; offset:8; size:4; signed:1;
field:unsigned int fd; offset:16; size:8; signed:0;
field:const char * buf; offset:24; size:8; signed:0;
field:size_t count; offset:32; size:8; signed:0;
print fmt: "fd: 0x%08lx, buf: 0x%08lx, count: 0x%08lx", ((unsigned long)(REC->fd)), ((unsigned long)(REC->buf)), ((unsigned long)(REC->count))
In this case we only care about the buf
argument, which is the text being written by
the write
syscall.
The tracepoint argument structure is in the format struct tracepoint__<type>__<name>
, for
syscalls:sys_enter_write
it would be struct tracepoint__syscalls__sys_enter_write
.
Gobpf doesn't provide the same "magic" functionality that bcc
provides, such as automagically
loading and attaching tracepoints, kprobes and uprobes for you, so you always need to manually
load and attach the functions you want to trace.
Loading and attaching the BPF does not mean that the functions are now being traced, you
need to also create a new table bcc.NewTable
and create a perf map bcc.InitPerfMap
,
once these have been created and initialised the BPF functions will now be loaded and
we start getting events from the traced functions.
- https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md
- https://github.com/iovisor/bpf-docs
- https://github.com/iovisor/bpf-docs/blob/master/bpf_helpers.rst