├── .github └── workflows │ ├── release.yml │ └── test.yml ├── .gitignore ├── .goreleaser.yml ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── compare.png ├── demo.gif ├── filter └── filter.go ├── go.mod ├── go.sum ├── main.go ├── printer ├── arg.go ├── array.go ├── colours.go ├── error.go ├── hexdump.go ├── object.go ├── printer.go ├── signal.go └── syscall.go ├── summary.go ├── summary.png ├── testdata ├── execve │ └── main.go └── pipe │ └── main.go ├── tracer ├── annotation │ ├── access.go │ ├── arch_prctl.go │ ├── arg.go │ ├── bpf.go │ ├── clockid.go │ ├── clone.go │ ├── closerange.go │ ├── control.go │ ├── device.go │ ├── dirent.go │ ├── epoll.go │ ├── error.go │ ├── eventfd.go │ ├── fadvice.go │ ├── fallocate.go │ ├── fanotify.go │ ├── fcntl.go │ ├── fd.go │ ├── flock.go │ ├── futex.go │ ├── hex.go │ ├── ioprio.go │ ├── ioring.go │ ├── kcmp.go │ ├── kexec.go │ ├── key.go │ ├── keyring.go │ ├── landlock.go │ ├── madvise.go │ ├── mbind.go │ ├── membarrier.go │ ├── memfd.go │ ├── mlock.go │ ├── mmap.go │ ├── module.go │ ├── mount.go │ ├── mremap.go │ ├── msg.go │ ├── msync.go │ ├── null.go │ ├── open.go │ ├── p2.go │ ├── perf.go │ ├── pkey.go │ ├── prctl.go │ ├── priority.go │ ├── prot.go │ ├── ptrace.go │ ├── quotactl.go │ ├── random.go │ ├── reboot.go │ ├── rlimit.go │ ├── rusage.go │ ├── sched.go │ ├── seccomp.go │ ├── sem.go │ ├── shm.go │ ├── shutdown.go │ ├── signal.go │ ├── sigstack.go │ ├── socket.go │ ├── splice.go │ ├── statx.go │ ├── syncfilerange.go │ ├── syslog.go │ ├── time.go │ ├── timer.go │ ├── wait.go │ ├── whence.go │ └── x.go ├── args.go ├── decode.go ├── decode_uint_test.go ├── netw │ ├── netw.go │ └── proc.go ├── signal.go ├── sys.go ├── sys_amd64.go ├── sys_arm64.go ├── sys_test.go ├── tracer.go ├── type.go ├── type_test.go ├── types_cap.go ├── types_cloneargs.go ├── types_data.go ├── types_epollevent.go ├── types_error.go ├── types_fdset.go ├── types_int.go ├── types_int_ptr.go ├── types_iocb.go ├── types_ioevents.go ├── types_iouring.go ├── types_iovec.go ├── types_itimerspec.go ├── types_itimerval.go ├── types_landlock.go ├── types_mountattr.go ├── types_mqattr.go ├── types_msghdr.go ├── types_openhow.go ├── types_pollfd.go ├── types_rlimit.go ├── types_rusage.go ├── types_schedattr.go ├── types_schedparam.go ├── types_sembuf.go ├── types_shmidds.go ├── types_sigaction.go ├── types_siginfo.go ├── types_sockaddr.go ├── types_sockoptval.go ├── types_stack.go ├── types_stat.go ├── types_statfs.go ├── types_statx.go ├── types_string.go ├── types_sysinfo.go ├── types_timespec.go ├── types_timeval.go ├── types_timex.go ├── types_timezone.go ├── types_tms.go ├── types_uname.go ├── types_userdesc.go ├── types_ustat.go ├── types_utimbuf.go └── types_waitstatus.go └── ux.png /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | build: 10 | name: releasing 11 | runs-on: ubuntu-22.04 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | fetch-depth: 0 17 | 18 | - uses: actions/setup-go@v3 19 | with: 20 | go-version: "1.19" 21 | - uses: goreleaser/goreleaser-action@v3 22 | with: 23 | version: latest 24 | args: release --rm-dist 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | jobs: 8 | test: 9 | name: tests 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | matrix: 13 | os: [ ubuntu-latest ] 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: actions/setup-go@v3 18 | with: 19 | go-version: '1.19' 20 | cache: true 21 | - name: Run tests 22 | run: make test 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /grace 3 | /dist 4 | /linux 5 | /headers -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - make headers 4 | 5 | builds: 6 | - id: grace 7 | main: . 8 | binary: grace 9 | ldflags: 10 | - "-linkmode external -s -w -extldflags '-fno-PIC -static'" 11 | env: 12 | - CGO_ENABLED=1 13 | - CGO_CFLAGS="-Iheaders/include" 14 | goos: 15 | - linux 16 | goarch: 17 | - "amd64" 18 | changelog: 19 | sort: asc 20 | filters: 21 | exclude: 22 | - "^docs:" 23 | - "^test:" 24 | 25 | archives: 26 | - format: binary 27 | name_template: "{{ .Binary}}-{{ .Os }}-{{ .Arch }}" 28 | 29 | release: 30 | prerelease: auto 31 | github: 32 | owner: liamg 33 | name: grace 34 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Useful Links 4 | 5 | - [List of syscalls with descriptions](https://linuxhint.com/list_of_linux_syscalls/) 6 | - [Syscall table](https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md) 7 | - [Another Syscall table](https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/) -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | 3 | RUN apt-get update -y && apt-get install -y git build-essential golang ca-certificates rsync 4 | RUN git clone --depth 1 https://github.com/torvalds/linux.git /linux 5 | COPY . /src 6 | WORKDIR /src 7 | RUN make test -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | default: build 2 | 3 | .PHONY: clean 4 | clean: 5 | rm -rf headers 6 | rm -rf linux 7 | 8 | .PHONY: test 9 | test: headers 10 | CGO_ENABLED=1 CGO_CFLAGS="-I$$(pwd)/headers/include" go test ./tracer ./printer ./filter 11 | 12 | linux: 13 | git clone --depth 1 https://github.com/torvalds/linux.git ./linux 14 | 15 | headers: linux 16 | cd linux && make headers_install ARCH=x86_64 INSTALL_HDR_PATH=../headers 17 | 18 | .PHONY: build 19 | build: headers 20 | CGO_ENABLED=1 CGO_CFLAGS="-I$$(pwd)/headers/include" go build --ldflags '-linkmode external -extldflags "-static"' 21 | 22 | .PHONY: install 23 | install: headers 24 | CGO_ENABLED=1 CGO_CFLAGS="-I$$(pwd)/headers/include" go install --ldflags '-linkmode external -extldflags "-static"' 25 | 26 | .PHONY: demo 27 | demo: build 28 | ./grace -- cat /dev/null 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # grace 2 | 3 | _grace_ is a tool for monitoring and annotating syscalls for a given process. 4 | 5 | It's essentially a lightweight [strace](https://en.wikipedia.org/wiki/Strace), in Go, with colours and pretty output. 6 | 7 |

8 | 9 |

10 | 11 |

12 | 13 |

14 | 15 | It's possible to tweak and filter the output to make it quite readable, for example (using `-vnmx`): 16 | 17 |

18 | 19 |

20 | 21 | You can also review a summary of encountered syscalls (and sort by various columns): 22 | 23 |

24 | 25 |

26 | 27 | 28 | ### grace vs. strace 29 | 30 | _grace_ isn't meant to compete with _strace_, it's purely meant to be a user-friendly, lightweight alternative. However, the following should provide a rough idea of what is supported in _grace_ so far. 31 | 32 | Over time grace is meant to become a simpler, more readable alternative to strace (_strace for dummies?_), albeit with reduced functionality/advanced features. 33 | 34 | | Feature | grace | strace | 35 | |---------------------------------------------------------------------------------------|-------|--------| 36 | | Start a program and print all syscalls it makes | ✅ | ✅ | 37 | | Attach to an existing process by `pid` and print all syscalls it makes | ✅ | ✅ | 38 | | Filter syscalls by name, e.g. only show occurrences of the `open` syscall | ✅ | ✅ | 39 | | Filter syscalls using a given path, e.g. only show syscalls that access `/etc/passwd` | ✅ | ✅ | 40 | | Dump I/O for certain file descriptors | ✅ | ✅ | 41 | | Count occurrences and duration of all syscalls and present in a useful format | ✅ | ✅ | 42 | | Print relative/absolute timestamps | ✅ | ✅ | 43 | | Tamper with syscalls | ❌ | ✅ | 44 | | Print extra information about file descriptors, such as path, socket addresses etc. | ✅ | ✅ | 45 | | Print stack traces | ❌ | ✅ | 46 | | Filter by return value | ✅ | ✅ | 47 | | Pretty colours to make output easier to read | ✅ | ❌ | 48 | | Lots of output options and customisation vectors | ✅ | ✅ | 49 | | Output to file | ✅ | ✅ | 50 | | Filter by failing/non-failing syscalls | ✅ | ✅ | 51 | 52 | _NOTE: Please feel free to add important strace features to this table, I'm working with a limited knowledge of strace._ 53 | 54 | 55 | 56 | ## Installation 57 | 58 | Grab a statically compiled binary from the [latest release](https://github.com/liamg/grace/releases/latest). 59 | 60 | ## Supported Platforms/Architecture 61 | 62 | Currently only Linux/amd64 is supported. Other architectures coming soon. 63 | 64 | If you'd like to implement a new architecture, you can duplicate `tracer/sys_amd64.go` and convert it to contain the syscall definitions for your arch. 65 | 66 | ### Usage Examples 67 | 68 | #### Trace a program 69 | 70 | ```bash 71 | grace -- cat /dev/null # replace 'cat /dev/null' with your program 72 | ``` 73 | 74 | #### Trace an existing process 75 | 76 | ```bash 77 | grace -p 123 # replace 123 with your pid 78 | 79 | # e.g. you could use pgrep to find the pid of a process 80 | grace -p `pgrep ping` 81 | ``` 82 | 83 | #### Trace a program and filter by syscall name 84 | 85 | ```bash 86 | grace -f "name=openat" -- cat /dev/null 87 | 88 | # you can also look for multiple syscalls 89 | grace -f "name=openat&name=close" -- cat /dev/null 90 | ``` 91 | 92 | #### Trace a program and filter by syscall name and path 93 | 94 | ```bash 95 | grace -f "name=openat&path=/dev/null" -- cat /dev/null 96 | ``` 97 | 98 | #### Trace a program and wire up stdin/out/err with the terminal 99 | 100 | ```bash 101 | grace -F -- cat 102 | ``` 103 | 104 | #### Trace a program with maximum readability options 105 | 106 | ```bash 107 | grace -vnmx -- cat /dev/null 108 | ``` 109 | 110 | #### Trace only failing syscalls 111 | 112 | ```bash 113 | grace -Z -- cat /dev/null 114 | ``` 115 | 116 | #### Show a summary of syscalls with durations, counts and errors 117 | 118 | ```bash 119 | grace -S -- cat /dev/null 120 | ``` 121 | 122 | ## Build Dependencies 123 | 124 | If you want to build _grace_ yourself instead of using the precompiled binaries, you'll need a recent version of Go (1.19+). Then `make build` is your friend. 125 | -------------------------------------------------------------------------------- /compare.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liamg/grace/a28e5c24b4666a6c6674c152a5435fbde628f6b2/compare.png -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liamg/grace/a28e5c24b4666a6c6674c152a5435fbde628f6b2/demo.gif -------------------------------------------------------------------------------- /filter/filter.go: -------------------------------------------------------------------------------- 1 | package filter 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | 8 | "github.com/liamg/grace/tracer" 9 | ) 10 | 11 | type Filter struct { 12 | allowNames []string 13 | allowPaths []string 14 | allowReturns []uint64 15 | failingOnly bool 16 | passingOnly bool 17 | } 18 | 19 | func Parse(input string) (*Filter, error) { 20 | filter := NewFilter() 21 | parts := strings.Split(input, "&") 22 | for _, part := range parts { 23 | if part == "" { 24 | continue 25 | } 26 | bits := strings.Split(part, "=") 27 | key := bits[0] 28 | value := bits[len(bits)-1] 29 | switch key { 30 | case "syscall", "name", "trace": 31 | filter.allowNames = append(filter.allowNames, strings.Split(value, ",")...) 32 | case "path": 33 | filter.allowPaths = append(filter.allowPaths, strings.Split(value, ",")...) 34 | case "ret", "retval", "return": 35 | ret, err := parseUint64(value) 36 | if err != nil { 37 | return nil, fmt.Errorf("failed to parse return value filter: %w", err) 38 | } 39 | filter.allowReturns = append(filter.allowReturns, ret) 40 | default: 41 | return nil, fmt.Errorf("invalid filter key: %s", key) 42 | } 43 | } 44 | return filter, nil 45 | } 46 | 47 | func parseUint64(input string) (uint64, error) { 48 | if strings.HasPrefix(input, "0x") { 49 | return strconv.ParseUint(input[2:], 16, 64) 50 | } 51 | return strconv.ParseUint(input, 10, 64) 52 | } 53 | 54 | func NewFilter() *Filter { 55 | return &Filter{} 56 | } 57 | 58 | func (f *Filter) Match(call *tracer.Syscall, exit bool) bool { 59 | 60 | if len(f.allowNames) > 0 { 61 | var match bool 62 | for _, name := range f.allowNames { 63 | if name == call.Name() { 64 | match = true 65 | break 66 | } 67 | } 68 | if !match { 69 | return false 70 | } 71 | } 72 | 73 | if len(f.allowPaths) > 0 { 74 | var match bool 75 | for _, path := range f.allowPaths { 76 | for _, realPath := range call.Paths() { 77 | if realPath == path { 78 | match = true 79 | break 80 | } 81 | } 82 | } 83 | if !match { 84 | return false 85 | } 86 | } 87 | 88 | if len(f.allowReturns) > 0 { 89 | if !exit { 90 | return false 91 | } 92 | var match bool 93 | for _, ret := range f.allowReturns { 94 | if uintptr(ret) == call.Return().Raw() { 95 | match = true 96 | break 97 | } 98 | } 99 | if !match { 100 | return false 101 | } 102 | } 103 | 104 | if (f.passingOnly || f.failingOnly) && !exit { 105 | return false 106 | } 107 | 108 | if f.failingOnly && call.Return().Int() >= 0 { 109 | return false 110 | } 111 | 112 | if f.passingOnly && call.Return().Int() < 0 { 113 | return false 114 | } 115 | 116 | // TODO check more filters 117 | 118 | return true 119 | } 120 | 121 | func (f *Filter) SetFailingOnly(failing bool) { 122 | f.failingOnly = failing 123 | } 124 | 125 | func (f *Filter) SetPassingOnly(passing bool) { 126 | f.passingOnly = passing 127 | } 128 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/liamg/grace 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/aquasecurity/table v1.8.0 7 | github.com/spf13/cobra v1.6.1 8 | github.com/stretchr/testify v1.8.1 9 | golang.org/x/sys v0.1.0 10 | ) 11 | 12 | require ( 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 15 | github.com/mattn/go-runewidth v0.0.13 // indirect 16 | github.com/pmezard/go-difflib v1.0.0 // indirect 17 | github.com/rivo/uniseg v0.2.0 // indirect 18 | github.com/spf13/pflag v1.0.5 // indirect 19 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect 20 | gopkg.in/yaml.v3 v3.0.1 // indirect 21 | ) 22 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aquasecurity/table v1.8.0 h1:9ntpSwrUfjrM6/YviArlx/ZBGd6ix8W+MtojQcM7tv0= 2 | github.com/aquasecurity/table v1.8.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 4 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= 8 | github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 9 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 10 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 11 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 12 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 13 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 14 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 15 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 16 | github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 17 | github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 18 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 19 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 20 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 21 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 22 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 23 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 24 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 25 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 26 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 27 | golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= 28 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 29 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM= 30 | golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 31 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 32 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 33 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 34 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 35 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 36 | -------------------------------------------------------------------------------- /printer/arg.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/liamg/grace/tracer" 7 | ) 8 | 9 | func (p *Printer) PrintArg(arg tracer.Arg, exit bool) { 10 | 11 | var indent int 12 | if p.multiline { 13 | indent = indentSize 14 | } 15 | 16 | if p.multiline { 17 | p.Print(strings.Repeat(" ", indent)) 18 | } 19 | 20 | if name := arg.Name(); name != "" { 21 | p.PrintDim("%s: ", name) 22 | } 23 | 24 | p.PrintArgValue(&arg, p.nextColour(), exit, 0, indent) 25 | } 26 | 27 | func (p *Printer) NewLine(indent int) { 28 | p.Print("\n" + strings.Repeat(" ", indent)) 29 | } 30 | 31 | func (p *Printer) PrintArgValue(arg *tracer.Arg, colour Colour, exit bool, propCount int, indent int) int { 32 | 33 | if p.rawOutput { 34 | p.PrintColour(colour, "0x%x", arg.Raw()) 35 | return propCount 36 | } 37 | 38 | if arg.ReplaceValueWithAnnotation() { 39 | p.PrintColour(colour, "%s", arg.Annotation()) 40 | return propCount 41 | } 42 | 43 | switch arg.Type() { 44 | case tracer.ArgTypeData: 45 | data := arg.Data() 46 | if p.maxStringLen > 0 && len(data) > p.maxStringLen { 47 | if p.hexDumpLongStrings { 48 | p.HexDump(arg.Raw(), arg.Data(), indent) 49 | return propCount 50 | } 51 | data = append(data[:p.maxStringLen], []byte("...")...) 52 | } 53 | p.PrintColour(colour, "%q", string(data)) 54 | //p.PrintDim(" @ 0x%x", arg.Raw()) 55 | case tracer.ArgTypeInt, tracer.ArgTypeLong, tracer.ArgTypeUnsignedInt, tracer.ArgTypeUnsignedLong, tracer.ArgTypeUnknown: 56 | p.PrintColour(colour, "%d", arg.Int()) 57 | case tracer.ArgTypeErrorCode: 58 | p.printError(arg) 59 | case tracer.ArgTypeAddress: 60 | p.PrintColour(colour, "0x%x", arg.Raw()) 61 | case tracer.ArgTypeObject: 62 | propCount += p.printObject(arg.Object(), colour, exit, propCount, indent) 63 | case tracer.ArgTypeArray: 64 | propCount += p.printArray(arg.Array(), colour, exit, propCount, indent) 65 | default: 66 | p.PrintColour(ColourRed, "UNKNOWN TYPE (raw=%d)", arg.Raw()) 67 | } 68 | 69 | if annotation := arg.Annotation(); annotation != "" { 70 | p.PrintDim(" -> %s", annotation) 71 | } 72 | 73 | return propCount 74 | } 75 | -------------------------------------------------------------------------------- /printer/array.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import "github.com/liamg/grace/tracer" 4 | 5 | func (p *Printer) printArray(arr []tracer.Arg, colour Colour, exit bool, count int, indent int) int { 6 | prevIndent := indent 7 | indent += indentSize 8 | p.PrintColour(colour, "[") 9 | 10 | for i, prop := range arr { 11 | if p.multiline { 12 | p.NewLine(indent) 13 | } 14 | colour := colours[i%len(colours)] 15 | p.PrintDim("%d", i) 16 | p.PrintDim(": ") 17 | count += p.PrintArgValue(&prop, colour, exit, count, indent+indentSize) 18 | if i < len(arr)-1 { 19 | p.PrintDim(", ") 20 | } 21 | if p.maxObjectProperties > 0 && count >= p.maxObjectProperties && count < len(arr) { 22 | p.PrintDim("...") 23 | break 24 | } 25 | count++ 26 | } 27 | if p.multiline && len(arr) > 0 { 28 | p.NewLine(prevIndent) 29 | } 30 | p.PrintColour(colour, "]") 31 | return count 32 | } 33 | -------------------------------------------------------------------------------- /printer/colours.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | type Colour int 4 | 5 | const ( 6 | ColourRed Colour = iota + 31 7 | ColourGreen 8 | ColourYellow 9 | ColourBlue 10 | ColourMagenta 11 | ColourCyan 12 | ColourWhite 13 | ) 14 | 15 | var ColourDefault Colour = 0 16 | 17 | var colours = []Colour{ 18 | ColourBlue, 19 | ColourYellow, 20 | ColourGreen, 21 | } 22 | 23 | func (p *Printer) currentColour() Colour { 24 | return colours[p.colourIndex%len(colours)] 25 | } 26 | 27 | func (p *Printer) nextColour() Colour { 28 | colour := colours[p.colourIndex%len(colours)] 29 | p.colourIndex++ 30 | return colour 31 | } 32 | -------------------------------------------------------------------------------- /printer/hexdump.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import "strings" 4 | 5 | const dumpWidth = 16 6 | 7 | func (p *Printer) HexDump(addr uintptr, data []byte, indent int) { 8 | 9 | var truncatedFrom uintptr 10 | if p.maxHexDumpLen > 0 && len(data) > p.maxHexDumpLen { 11 | truncatedFrom = uintptr(len(data)) 12 | data = data[:p.maxHexDumpLen] 13 | } 14 | 15 | startAddr := addr - (addr % dumpWidth) 16 | endAddr := addr + uintptr(len(data)) 17 | if endAddr%dumpWidth > 0 { 18 | endAddr += dumpWidth - (endAddr % dumpWidth) 19 | } 20 | p.Print(strings.Repeat(" ", indent)) 21 | p.Print("(see below hexdump)") 22 | p.NewLine(indent) 23 | p.Print(" ") 24 | for i := 0; i < dumpWidth; i++ { 25 | p.PrintDim("%02x ", i) 26 | } 27 | for i := startAddr; i < endAddr; i += dumpWidth { 28 | p.NewLine(indent) 29 | p.PrintDim("%16x: ", i) 30 | for j := 0; j < dumpWidth; j++ { 31 | local := (i + uintptr(j)) - addr 32 | if i+uintptr(j) < addr || local >= uintptr(len(data)) { 33 | p.PrintDim(".. ") 34 | } else { 35 | p.PrintColour(ColourRed, "%02x ", data[local]) 36 | } 37 | } 38 | for j := 0; j < dumpWidth; j++ { 39 | local := (i + uintptr(j)) - addr 40 | if i+uintptr(j) < addr || local >= uintptr(len(data)) { 41 | p.PrintDim(".") 42 | } else { 43 | c := data[local] 44 | if c < 32 || c > 126 { 45 | c = '.' 46 | } 47 | p.PrintColour(ColourBlue, "%c", c) 48 | } 49 | } 50 | } 51 | if truncatedFrom > 0 { 52 | p.PrintDim("\n... (truncated from %d bytes -> %d bytes) ...", truncatedFrom, p.maxHexDumpLen) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /printer/object.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "github.com/liamg/grace/tracer" 5 | ) 6 | 7 | func (p *Printer) printObject(obj *tracer.Object, colour Colour, exit bool, count int, indent int) int { 8 | if obj == nil { 9 | p.PrintDim("NULL") 10 | return count 11 | } 12 | prevIndent := indent 13 | indent += indentSize 14 | p.PrintColour(colour, "%s{", obj.Name) 15 | 16 | for i, prop := range obj.Properties { 17 | if p.multiline { 18 | p.NewLine(indent) 19 | } 20 | colour := colours[i%len(colours)] 21 | p.PrintDim("%s", prop.Name()) 22 | p.PrintDim(": ") 23 | count += p.PrintArgValue(&prop, colour, exit, count, indent+indentSize) 24 | if i < len(obj.Properties)-1 { 25 | p.PrintDim(", ") 26 | } 27 | if p.maxObjectProperties > 0 && count >= p.maxObjectProperties { 28 | p.PrintDim("...") 29 | break 30 | } 31 | count++ 32 | } 33 | if p.multiline { 34 | p.NewLine(prevIndent) 35 | } 36 | p.PrintColour(colour, "}") 37 | return count 38 | } 39 | -------------------------------------------------------------------------------- /printer/printer.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "time" 7 | 8 | "github.com/liamg/grace/tracer" 9 | ) 10 | 11 | type Printer struct { 12 | w io.Writer 13 | useColours bool 14 | maxStringLen int 15 | hexDumpLongStrings bool 16 | maxHexDumpLen int 17 | maxObjectProperties int 18 | colourIndex int 19 | argProgress int 20 | extraNewLine bool 21 | multiline bool 22 | inSyscall bool 23 | filter Filter 24 | lastEntryMatchedFilter bool 25 | relativeTimestamps bool 26 | absoluteTimestamps bool 27 | startTime time.Time 28 | showNumbers bool 29 | rawOutput bool 30 | } 31 | 32 | type Filter interface { 33 | Match(syscall *tracer.Syscall, exit bool) bool 34 | } 35 | 36 | func New(w io.Writer) *Printer { 37 | return &Printer{ 38 | w: w, 39 | useColours: true, 40 | maxStringLen: 32, 41 | hexDumpLongStrings: true, 42 | maxHexDumpLen: 4096, 43 | maxObjectProperties: 2, 44 | startTime: time.Now(), 45 | } 46 | } 47 | 48 | const indentSize = 4 49 | 50 | func (p *Printer) SetUseColours(useColours bool) { 51 | p.useColours = useColours 52 | } 53 | 54 | func (p *Printer) SetMaxStringLen(maxStringLen int) { 55 | p.maxStringLen = maxStringLen 56 | } 57 | 58 | func (p *Printer) SetHexDumpLongStrings(hexDumpLongStrings bool) { 59 | p.hexDumpLongStrings = hexDumpLongStrings 60 | } 61 | 62 | func (p *Printer) SetMaxHexDumpLen(maxHexDumpLen int) { 63 | p.maxHexDumpLen = maxHexDumpLen 64 | } 65 | 66 | func (p *Printer) SetMaxObjectProperties(maxObjectProperties int) { 67 | p.maxObjectProperties = maxObjectProperties 68 | } 69 | 70 | func (p *Printer) SetExtraNewLine(extraNewLine bool) { 71 | p.extraNewLine = extraNewLine 72 | } 73 | 74 | func (p *Printer) SetShowAbsoluteTimestamps(timestamps bool) { 75 | p.absoluteTimestamps = timestamps 76 | } 77 | 78 | func (p *Printer) SetShowRelativeTimestamps(timestamps bool) { 79 | p.relativeTimestamps = timestamps 80 | } 81 | 82 | func (p *Printer) SetMultiLine(multiline bool) { 83 | p.multiline = multiline 84 | } 85 | 86 | func (p *Printer) SetFilter(filter Filter) { 87 | p.filter = filter 88 | } 89 | 90 | func (p *Printer) SetShowSyscallNumber(number bool) { 91 | p.showNumbers = number 92 | } 93 | 94 | func (p *Printer) SetRawOutput(output bool) { 95 | p.rawOutput = output 96 | } 97 | 98 | func (p *Printer) PrefixEvent() { 99 | if p.relativeTimestamps { 100 | p.PrintDim("%12s ", time.Since(p.startTime)) 101 | } 102 | if p.absoluteTimestamps { 103 | p.PrintDim("%18s ", time.Now().Format("15:04:05.999999999")) 104 | } 105 | } 106 | 107 | func (p *Printer) Print(format string, args ...interface{}) { 108 | _, _ = fmt.Fprintf(p.w, format, args...) 109 | } 110 | 111 | func (p *Printer) PrintDim(format string, args ...interface{}) { 112 | p.PrintColour(2, format, args...) 113 | } 114 | 115 | func (p *Printer) PrintColour(colour Colour, format string, args ...interface{}) { 116 | if p.useColours { 117 | p.Print("\x1b[%dm", colour) 118 | } 119 | p.Print(format, args...) 120 | if p.useColours { 121 | p.Print("\x1b[0m") 122 | } 123 | } 124 | 125 | func (p *Printer) PrintProcessExit(i int) { 126 | colour := ColourGreen 127 | if i != 0 { 128 | colour = ColourRed 129 | } 130 | if p.inSyscall { 131 | p.PrintDim(" = ?\n") 132 | } 133 | if p.multiline { 134 | p.Print("\n") 135 | } 136 | p.PrintColour(colour, "Process exited with status %d\n", i) 137 | } 138 | 139 | func (p *Printer) PrintAttach(pid int) { 140 | p.PrintColour(ColourYellow, "Attached to process %d\n", pid) 141 | if p.multiline { 142 | p.Print("\n") 143 | } 144 | } 145 | 146 | func (p *Printer) PrintDetach(pid int) { 147 | p.PrintColour(ColourYellow, "Detached from process %d\n", pid) 148 | if p.multiline { 149 | p.Print("\n") 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /printer/signal.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "syscall" 5 | 6 | "github.com/liamg/grace/tracer" 7 | "github.com/liamg/grace/tracer/annotation" 8 | ) 9 | 10 | func (p *Printer) PrintSignal(signal *tracer.SigInfo) { 11 | p.PrefixEvent() 12 | p.PrintColour(ColourMagenta, "--> ") 13 | p.PrintColour( 14 | ColourCyan, 15 | "SIGNAL: %s (code=%s, pid=%d, uid=%d)", 16 | annotation.SignalToString(int(signal.Signo)), 17 | annotation.SignalCodeToString(syscall.Signal(signal.Signo), signal.Code), 18 | signal.Pid, 19 | signal.Uid, 20 | ) 21 | p.PrintColour(ColourMagenta, " <--\n") 22 | if p.multiline { 23 | p.Print("\n") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /printer/syscall.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/liamg/grace/tracer" 7 | ) 8 | 9 | func (p *Printer) PrintSyscallEnter(syscall *tracer.Syscall) { 10 | p.printSyscallEnter(syscall, false) 11 | } 12 | 13 | func (p *Printer) printSyscallEnter(syscall *tracer.Syscall, overrideFilter bool) { 14 | 15 | if !overrideFilter { 16 | if p.filter != nil { 17 | if !p.filter.Match(syscall, false) { 18 | p.lastEntryMatchedFilter = false 19 | return 20 | } 21 | } 22 | p.lastEntryMatchedFilter = true 23 | } 24 | 25 | p.PrefixEvent() 26 | 27 | p.colourIndex = 0 28 | p.argProgress = 0 29 | 30 | if p.showNumbers { 31 | p.PrintDim("%4s", fmt.Sprintf("%d ", syscall.Number())) 32 | } 33 | 34 | if syscall.Unknown() { 35 | p.PrintColour(ColourRed, syscall.Name()) 36 | } else { 37 | p.PrintColour(ColourDefault, syscall.Name()) 38 | } 39 | p.printRemainingArgs(syscall, false) 40 | p.inSyscall = true 41 | } 42 | 43 | func (p *Printer) PrintSyscallExit(syscall *tracer.Syscall) { 44 | 45 | if p.filter != nil { 46 | if !p.lastEntryMatchedFilter && !p.filter.Match(syscall, true) { 47 | return 48 | } 49 | } 50 | 51 | if !p.lastEntryMatchedFilter { 52 | p.printSyscallEnter(syscall, true) 53 | } 54 | 55 | p.printRemainingArgs(syscall, true) 56 | p.PrintDim(" = ") 57 | ret := syscall.Return() 58 | p.PrintArgValue(&ret, ColourGreen, true, 0, 0) 59 | p.Print("\n") 60 | if p.extraNewLine { 61 | p.Print("\n") 62 | } 63 | p.inSyscall = false 64 | } 65 | 66 | func (p *Printer) printRemainingArgs(syscall *tracer.Syscall, exit bool) { 67 | if !exit { 68 | p.PrintDim("(") 69 | } 70 | var remaining []tracer.Arg 71 | if p.argProgress < len(syscall.Args()) { 72 | remaining = syscall.Args()[p.argProgress:] 73 | for i, arg := range remaining { 74 | if !arg.Known() { 75 | break 76 | } 77 | if p.argProgress == 0 && p.multiline { 78 | p.Print("\n") 79 | } 80 | p.PrintArg(arg, exit) 81 | if i < len(remaining)-1 || !syscall.Complete() { 82 | p.PrintDim(", ") 83 | } 84 | p.argProgress++ 85 | if p.multiline { 86 | p.Print("\n") 87 | } 88 | } 89 | } 90 | 91 | if ((exit && len(remaining) > 0) || (!exit && p.argProgress == len(syscall.Args()))) && syscall.Complete() { 92 | p.PrintDim(")") 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /summary.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "sort" 7 | "time" 8 | 9 | "github.com/aquasecurity/table" 10 | "github.com/liamg/grace/tracer" 11 | ) 12 | 13 | func configureSummary(t *tracer.Tracer, w io.Writer, sortKey string) { 14 | 15 | tracker := &tracker{ 16 | counts: make(map[string]int), 17 | errors: make(map[string]int), 18 | durations: make(map[string]time.Duration), 19 | starts: make(map[string]time.Time), 20 | } 21 | 22 | t.SetSyscallEnterHandler(tracker.recordEnter) 23 | t.SetSyscallExitHandler(tracker.recordExit) 24 | t.SetDetachHandler(func(i int) { 25 | tracker.print(w, sortKey) 26 | }) 27 | } 28 | 29 | type tracker struct { 30 | counts map[string]int 31 | errors map[string]int 32 | durations map[string]time.Duration 33 | starts map[string]time.Time 34 | } 35 | 36 | func (t *tracker) recordEnter(s *tracer.Syscall) { 37 | t.starts[s.Name()] = time.Now() 38 | } 39 | 40 | func (t *tracker) recordExit(s *tracer.Syscall) { 41 | stop := time.Now() 42 | if start, ok := t.starts[s.Name()]; ok { 43 | t.durations[s.Name()] += stop.Sub(start) 44 | delete(t.starts, s.Name()) 45 | } 46 | if s.Return().Int() < 0 { 47 | t.errors[s.Name()]++ 48 | } 49 | t.counts[s.Name()]++ 50 | } 51 | 52 | func (t *tracker) print(w io.Writer, sortKey string) { 53 | 54 | tab := table.New(w) 55 | tab.SetRowLines(false) 56 | tab.AddHeaders("time %", "seconds", "usecs/call", "count", "errors", "syscall") 57 | tab.SetAlignment(table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignRight, table.AlignLeft) 58 | tab.SetLineStyle(table.StyleBlue) 59 | 60 | var total time.Duration 61 | for _, duration := range t.durations { 62 | total += duration 63 | } 64 | 65 | type row struct { 66 | sortKey int 67 | cols []string 68 | name string 69 | } 70 | 71 | var rows []row 72 | 73 | for name, count := range t.counts { 74 | 75 | duration := t.durations[name] 76 | 77 | percent := float64(duration) * 100 / float64(total) 78 | 79 | var key int 80 | switch sortKey { 81 | case "count": 82 | key = count 83 | case "time": 84 | key = int(percent * 100000) 85 | case "seconds": 86 | key = int(duration) 87 | case "errors": 88 | key = t.errors[name] 89 | 90 | } 91 | 92 | rows = append(rows, row{ 93 | name: name, 94 | sortKey: key, 95 | cols: []string{ 96 | fmt.Sprintf("%.2f", percent), 97 | fmt.Sprintf("%.6f", duration.Seconds()), 98 | fmt.Sprintf("%d", duration.Microseconds()/int64(count)), 99 | fmt.Sprintf("%d", count), 100 | fmt.Sprintf("%d", t.errors[name]), 101 | name, 102 | }, 103 | }) 104 | } 105 | 106 | if sortKey == "" { 107 | sort.Slice(rows, func(i, j int) bool { 108 | return rows[i].name < rows[j].name 109 | }) 110 | } else { 111 | sort.Slice(rows, func(i, j int) bool { 112 | return rows[i].sortKey > rows[j].sortKey 113 | }) 114 | } 115 | 116 | for _, row := range rows { 117 | tab.AddRow(row.cols...) 118 | } 119 | 120 | tab.Render() 121 | } 122 | -------------------------------------------------------------------------------- /summary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liamg/grace/a28e5c24b4666a6c6674c152a5435fbde628f6b2/summary.png -------------------------------------------------------------------------------- /testdata/execve/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "syscall" 6 | ) 7 | 8 | func main() { 9 | _ = syscall.Exec("/usr/bin/ls", []string{"ls", "/tmp"}, os.Environ()) 10 | } 11 | -------------------------------------------------------------------------------- /testdata/pipe/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | ) 7 | 8 | func main() { 9 | var buf syscall.Utsname 10 | _ = syscall.Uname(&buf) 11 | printUtsField(buf.Release) 12 | } 13 | 14 | func printUtsField(f [65]int8) { 15 | var str []byte 16 | for i := 0; i < len(f); i++ { 17 | str = append(str, byte(f[i])) 18 | } 19 | fmt.Println(string(str)) 20 | } 21 | -------------------------------------------------------------------------------- /tracer/annotation/access.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func AnnotateAccMode(arg Arg, _ int) { 10 | var joins []string 11 | if arg.Raw() == unix.F_OK { 12 | joins = append(joins, "F_OK") 13 | } else { 14 | if arg.Raw()&unix.R_OK > 0 { 15 | joins = append(joins, "R_OK") 16 | } 17 | if arg.Raw()&unix.W_OK > 0 { 18 | joins = append(joins, "W_OK") 19 | } 20 | if arg.Raw()&unix.X_OK > 0 { 21 | joins = append(joins, "X_OK") 22 | } 23 | } 24 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 25 | } 26 | -------------------------------------------------------------------------------- /tracer/annotation/arch_prctl.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | 6 | #ifndef _ASM_X86_PRCTL_H 7 | #define _ASM_X86_PRCTL_H 8 | #define ARCH_SET_GS 0x1001 9 | #define ARCH_SET_FS 0x1002 10 | #define ARCH_GET_FS 0x1003 11 | #define ARCH_GET_GS 0x1004 12 | #endif 13 | 14 | int iARCH_SET_GS = ARCH_SET_GS; 15 | int iARCH_SET_FS = ARCH_SET_FS; 16 | int iARCH_GET_FS = ARCH_GET_FS; 17 | int iARCH_GET_GS = ARCH_GET_GS; 18 | */ 19 | import "C" 20 | 21 | var archPrCodes = map[int]string{ 22 | int(C.iARCH_SET_GS): "ARCH_SET_GS", 23 | int(C.iARCH_SET_FS): "ARCH_SET_FS", 24 | int(C.iARCH_GET_FS): "ARCH_GET_FS", 25 | int(C.iARCH_GET_GS): "ARCH_GET_GS", 26 | } 27 | 28 | func AnnotateArchPrctrlCode(arg Arg, pid int) { 29 | if s, ok := archPrCodes[int(arg.Raw())]; ok { 30 | arg.SetAnnotation(s, true) 31 | } else { 32 | AnnotateHex(arg, pid) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tracer/annotation/arg.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | type Arg interface { 4 | Raw() uintptr 5 | SetAnnotation(annotation string, replace bool) 6 | } 7 | -------------------------------------------------------------------------------- /tracer/annotation/bpf.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var bpfCmds = map[int]string{ 6 | unix.BPF_MAP_CREATE: "BPF_MAP_CREATE", 7 | unix.BPF_MAP_LOOKUP_ELEM: "BPF_MAP_LOOKUP_ELEM", 8 | unix.BPF_MAP_UPDATE_ELEM: "BPF_MAP_UPDATE_ELEM", 9 | unix.BPF_MAP_DELETE_ELEM: "BPF_MAP_DELETE_ELEM", 10 | unix.BPF_MAP_GET_NEXT_KEY: "BPF_MAP_GET_NEXT_KEY", 11 | unix.BPF_PROG_LOAD: "BPF_PROG_LOAD", 12 | unix.BPF_OBJ_PIN: "BPF_OBJ_PIN", 13 | unix.BPF_OBJ_GET: "BPF_OBJ_GET", 14 | unix.BPF_PROG_ATTACH: "BPF_PROG_ATTACH", 15 | unix.BPF_PROG_DETACH: "BPF_PROG_DETACH", 16 | unix.BPF_PROG_TEST_RUN: "BPF_PROG_TEST_RUN", 17 | unix.BPF_PROG_GET_NEXT_ID: "BPF_PROG_GET_NEXT_ID", 18 | unix.BPF_MAP_GET_NEXT_ID: "BPF_MAP_GET_NEXT_ID", 19 | unix.BPF_PROG_GET_FD_BY_ID: "BPF_PROG_GET_FD_BY_ID", 20 | unix.BPF_MAP_GET_FD_BY_ID: "BPF_MAP_GET_FD_BY_ID", 21 | unix.BPF_OBJ_GET_INFO_BY_FD: "BPF_OBJ_GET_INFO_BY_FD", 22 | unix.BPF_PROG_QUERY: "BPF_PROG_QUERY", 23 | unix.BPF_RAW_TRACEPOINT_OPEN: "BPF_RAW_TRACEPOINT_OPEN", 24 | unix.BPF_BTF_LOAD: "BPF_BTF_LOAD", 25 | unix.BPF_BTF_GET_FD_BY_ID: "BPF_BTF_GET_FD_BY_ID", 26 | unix.BPF_TASK_FD_QUERY: "BPF_TASK_FD_QUERY", 27 | unix.BPF_MAP_LOOKUP_AND_DELETE_ELEM: "BPF_MAP_LOOKUP_AND_DELETE_ELEM", 28 | unix.BPF_MAP_FREEZE: "BPF_MAP_FREEZE", 29 | unix.BPF_BTF_GET_NEXT_ID: "BPF_BTF_GET_NEXT_ID", 30 | unix.BPF_MAP_LOOKUP_BATCH: "BPF_MAP_LOOKUP_BATCH", 31 | unix.BPF_MAP_LOOKUP_AND_DELETE_BATCH: "BPF_MAP_LOOKUP_AND_DELETE_BATCH", 32 | unix.BPF_MAP_UPDATE_BATCH: "BPF_MAP_UPDATE_BATCH", 33 | unix.BPF_MAP_DELETE_BATCH: "BPF_MAP_DELETE_BATCH", 34 | unix.BPF_LINK_CREATE: "BPF_LINK_CREATE", 35 | unix.BPF_LINK_UPDATE: "BPF_LINK_UPDATE", 36 | unix.BPF_LINK_GET_FD_BY_ID: "BPF_LINK_GET_FD_BY_ID", 37 | unix.BPF_LINK_GET_NEXT_ID: "BPF_LINK_GET_NEXT_ID", 38 | unix.BPF_ENABLE_STATS: "BPF_ENABLE_STATS", 39 | unix.BPF_ITER_CREATE: "BPF_ITER_CREATE", 40 | unix.BPF_LINK_DETACH: "BPF_LINK_DETACH", 41 | } 42 | 43 | func AnnotateBPFCmd(arg Arg, _ int) { 44 | if v, ok := bpfCmds[int(arg.Raw())]; ok { 45 | arg.SetAnnotation(v, true) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tracer/annotation/clockid.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var clockIds = map[int]string{ 6 | unix.CLOCK_REALTIME: "CLOCK_REALTIME", 7 | unix.CLOCK_MONOTONIC: "CLOCK_MONOTONIC", 8 | unix.CLOCK_PROCESS_CPUTIME_ID: "CLOCK_PROCESS_CPUTIME_ID", 9 | unix.CLOCK_THREAD_CPUTIME_ID: "CLOCK_THREAD_CPUTIME_ID", 10 | unix.CLOCK_MONOTONIC_RAW: "CLOCK_MONOTONIC_RAW", 11 | unix.CLOCK_REALTIME_COARSE: "CLOCK_REALTIME_COARSE", 12 | unix.CLOCK_MONOTONIC_COARSE: "CLOCK_MONOTONIC_COARSE", 13 | unix.CLOCK_BOOTTIME: "CLOCK_BOOTTIME", 14 | unix.CLOCK_REALTIME_ALARM: "CLOCK_REALTIME_ALARM", 15 | unix.CLOCK_BOOTTIME_ALARM: "CLOCK_BOOTTIME_ALARM", 16 | } 17 | 18 | func AnnotateClockID(arg Arg, pid int) { 19 | if str, ok := clockIds[int(arg.Raw())]; ok { 20 | arg.SetAnnotation(str, true) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tracer/annotation/clone.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var cloneFlags = map[int]string{ 10 | unix.CLONE_CHILD_CLEARTID: "CLONE_CHILD_CLEARTID", 11 | unix.CLONE_CHILD_SETTID: "CLONE_CHILD_SETTID", 12 | unix.CLONE_FILES: "CLONE_FILES", 13 | unix.CLONE_FS: "CLONE_FS", 14 | unix.CLONE_IO: "CLONE_IO", 15 | unix.CLONE_NEWCGROUP: "CLONE_NEWCGROUP", 16 | unix.CLONE_NEWIPC: "CLONE_NEWIPC", 17 | unix.CLONE_NEWNET: "CLONE_NEWNET", 18 | unix.CLONE_NEWNS: "CLONE_NEWNS", 19 | unix.CLONE_NEWPID: "CLONE_NEWPID", 20 | unix.CLONE_NEWUSER: "CLONE_NEWUSER", 21 | unix.CLONE_NEWUTS: "CLONE_NEWUTS", 22 | unix.CLONE_PARENT: "CLONE_PARENT", 23 | unix.CLONE_PARENT_SETTID: "CLONE_PARENT_SETTID", 24 | unix.CLONE_PTRACE: "CLONE_PTRACE", 25 | unix.CLONE_SETTLS: "CLONE_SETTLS", 26 | unix.CLONE_SIGHAND: "CLONE_SIGHAND", 27 | unix.CLONE_SYSVSEM: "CLONE_SYSVSEM", 28 | unix.CLONE_THREAD: "CLONE_THREAD", 29 | unix.CLONE_UNTRACED: "CLONE_UNTRACED", 30 | unix.CLONE_VFORK: "CLONE_VFORK", 31 | unix.CLONE_VM: "CLONE_VM", 32 | } 33 | 34 | func AnnotateCloneFlags(arg Arg, _ int) { 35 | var joins []string 36 | for flag, name := range cloneFlags { 37 | if arg.Raw()&uintptr(flag) != 0 { 38 | joins = append(joins, name) 39 | } 40 | } 41 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 42 | } 43 | -------------------------------------------------------------------------------- /tracer/annotation/closerange.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var closeRangeFlags = map[int]string{ 10 | unix.CLOSE_RANGE_CLOEXEC: "CLOSE_RANGE_CLOEXEC", 11 | unix.CLOSE_RANGE_UNSHARE: "CLOSE_RANGE_UNSHARE", 12 | } 13 | 14 | func AnnotateCloseRangeFlags(arg Arg, _ int) { 15 | var joins []string 16 | for flag, str := range closeRangeFlags { 17 | if int(arg.Raw())&flag != 0 { 18 | joins = append(joins, str) 19 | } 20 | } 21 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 22 | } 23 | -------------------------------------------------------------------------------- /tracer/annotation/control.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var socketLevels = map[int]string{ 6 | unix.SOL_SOCKET: "SOL_SOCKET", 7 | unix.SOL_AAL: "SOL_AAL", 8 | unix.SOL_ALG: "SOL_ALG", 9 | unix.SOL_ATM: "SOL_ATM", 10 | unix.SOL_CAIF: "SOL_CAIF", 11 | unix.SOL_CAN_BASE: "SOL_CAN_BASE", 12 | unix.SOL_CAN_RAW: "SOL_CAN_RAW", 13 | unix.SOL_DCCP: "SOL_DCCP", 14 | unix.SOL_DECNET: "SOL_DECNET", 15 | unix.SOL_ICMPV6: "SOL_ICMPV6", 16 | unix.SOL_IP: "SOL_IP", 17 | unix.SOL_IPV6: "SOL_IPV6", 18 | unix.SOL_IRDA: "SOL_IRDA", 19 | unix.SOL_IUCV: "SOL_IUCV", 20 | unix.SOL_KCM: "SOL_KCM", 21 | unix.SOL_LLC: "SOL_LLC", 22 | unix.SOL_MCTP: "SOL_MCTP", 23 | unix.SOL_MPTCP: "SOL_MPTCP", 24 | unix.SOL_NETBEUI: "SOL_NETBEUI", 25 | unix.SOL_NETLINK: "SOL_NETLINK", 26 | unix.SOL_NFC: "SOL_NFC", 27 | unix.SOL_PACKET: "SOL_PACKET", 28 | unix.SOL_PNPIPE: "SOL_PNPIPE", 29 | unix.SOL_PPPOL2TP: "SOL_PPPOL2TP", 30 | unix.SOL_RAW: "SOL_RAW", 31 | unix.SOL_RDS: "SOL_RDS", 32 | unix.SOL_RXRPC: "SOL_RXRPC", 33 | unix.SOL_SMC: "SOL_SMC", 34 | unix.SOL_TCP: "SOL_TCP", 35 | unix.SOL_TIPC: "SOL_TIPC", 36 | unix.SOL_TLS: "SOL_TLS", 37 | unix.SOL_X25: "SOL_X25", 38 | unix.SOL_XDP: "SOL_XDP", 39 | } 40 | 41 | func AnnotateSocketLevel(arg Arg, _ int) { 42 | if str, ok := socketLevels[int(arg.Raw())]; ok { 43 | arg.SetAnnotation(str, true) 44 | } 45 | } 46 | 47 | var cmsgTypes = map[int]string{ 48 | unix.SCM_RIGHTS: "SCM_RIGHTS", 49 | unix.SCM_CREDENTIALS: "SCM_CREDENTIALS", 50 | unix.SCM_TIMESTAMP: "SCM_TIMESTAMP", 51 | } 52 | 53 | func AnnotateControlMessageType(arg Arg, _ int) { 54 | if str, ok := cmsgTypes[int(arg.Raw())]; ok { 55 | arg.SetAnnotation(str, true) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tracer/annotation/device.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "fmt" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func AnnotateDevice(arg Arg, pid int) { 10 | arg.SetAnnotation(DeviceToString(uint64(arg.Raw())), true) 11 | } 12 | 13 | func DeviceToString(dev uint64) string { 14 | major := "0" 15 | if m := unix.Major(dev); m != 0 { 16 | major = fmt.Sprintf("0x%x", m) 17 | } 18 | minor := "0" 19 | if m := unix.Minor(dev); m != 0 { 20 | minor = fmt.Sprintf("0x%x", m) 21 | } 22 | return fmt.Sprintf("makedev(%s, %s)", major, minor) 23 | } 24 | -------------------------------------------------------------------------------- /tracer/annotation/dirent.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var dirEntTypes = map[int]string{ 6 | unix.DT_BLK: "DT_BLK", 7 | unix.DT_CHR: "DT_CHR", 8 | unix.DT_DIR: "DT_DIR", 9 | unix.DT_FIFO: "DT_FIFO", 10 | unix.DT_LNK: "DT_LNK", 11 | unix.DT_REG: "DT_REG", 12 | unix.DT_SOCK: "DT_SOCK", 13 | unix.DT_WHT: "DT_WHT", 14 | unix.DT_UNKNOWN: "DT_UNKNOWN", 15 | } 16 | 17 | func AnnotateDirEntType(arg Arg, _ int) { 18 | if str, ok := dirEntTypes[int(arg.Raw())]; ok { 19 | arg.SetAnnotation(str, true) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tracer/annotation/epoll.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var epollCtlOps = map[int]string{ 6 | unix.EPOLL_CTL_ADD: "EPOLL_CTL_ADD", 7 | unix.EPOLL_CTL_DEL: "EPOLL_CTL_DEL", 8 | unix.EPOLL_CTL_MOD: "EPOLL_CTL_MOD", 9 | } 10 | 11 | func AnnotateEpollCtlOp(arg Arg, _ int) { 12 | if str, ok := epollCtlOps[int(arg.Raw())]; ok { 13 | arg.SetAnnotation(str, true) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tracer/annotation/eventfd.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var eventFdFlags = map[int]string{ 10 | unix.O_CLOEXEC: "EFD_CLOEXEC", 11 | unix.O_NONBLOCK: "EFD_NONBLOCK", 12 | unix.EFD_SEMAPHORE: "EFD_SEMAPHORE", 13 | } 14 | 15 | func AnnotateEventFdFlags(arg Arg, _ int) { 16 | var joins []string 17 | for flag, str := range eventFdFlags { 18 | if int(arg.Raw())&flag != 0 { 19 | joins = append(joins, str) 20 | } 21 | } 22 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 23 | } 24 | -------------------------------------------------------------------------------- /tracer/annotation/fadvice.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | 8 | var fadviceFlags = map[int]string{ 9 | C.POSIX_FADV_NORMAL: "POSIX_FADV_NORMAL", 10 | C.POSIX_FADV_RANDOM: "POSIX_FADV_RANDOM", 11 | C.POSIX_FADV_SEQUENTIAL: "POSIX_FADV_SEQUENTIAL", 12 | C.POSIX_FADV_WILLNEED: "POSIX_FADV_WILLNEED", 13 | C.POSIX_FADV_DONTNEED: "POSIX_FADV_DONTNEED", 14 | C.POSIX_FADV_NOREUSE: "POSIX_FADV_NOREUSE", 15 | } 16 | 17 | func AnnotateFAdvice(arg Arg, pid int) { 18 | if str, ok := fadviceFlags[int(arg.Raw())]; ok { 19 | arg.SetAnnotation(str, true) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tracer/annotation/fallocate.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var fallocateModes = map[int]string{ 6 | unix.FALLOC_FL_KEEP_SIZE: "FALLOC_FL_KEEP_SIZE", 7 | unix.FALLOC_FL_PUNCH_HOLE: "FALLOC_FL_PUNCH_HOLE", 8 | unix.FALLOC_FL_COLLAPSE_RANGE: "FALLOC_FL_COLLAPSE_RANGE", 9 | unix.FALLOC_FL_ZERO_RANGE: "FALLOC_FL_ZERO_RANGE", 10 | unix.FALLOC_FL_INSERT_RANGE: "FALLOC_FL_INSERT_RANGE", 11 | unix.FALLOC_FL_UNSHARE_RANGE: "FALLOC_FL_UNSHARE_RANGE", 12 | } 13 | 14 | func AnnotateFallocateMode(arg Arg, _ int) { 15 | if str, ok := fallocateModes[int(arg.Raw())]; ok { 16 | arg.SetAnnotation(str, true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tracer/annotation/fanotify.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var fanotifyFlags = map[int]string{ 10 | unix.FAN_CLASS_PRE_CONTENT: "FAN_CLASS_PRE_CONTENT", 11 | unix.FAN_CLASS_CONTENT: "FAN_CLASS_CONTENT", 12 | unix.FAN_CLASS_NOTIF: "FAN_CLASS_NOTIF", 13 | unix.FAN_REPORT_FID: "FAN_REPORT_FID", 14 | unix.FAN_REPORT_NAME: "FAN_REPORT_NAME", 15 | unix.FAN_REPORT_DIR_FID: "FAN_REPORT_DIR_FID", 16 | unix.FAN_REPORT_TID: "FAN_REPORT_TID", 17 | unix.FAN_NONBLOCK: "FAN_NONBLOCK", 18 | unix.FAN_UNLIMITED_QUEUE: "FAN_UNLIMITED_QUEUE", 19 | unix.FAN_UNLIMITED_MARKS: "FAN_UNLIMITED_MARKS", 20 | unix.FAN_ENABLE_AUDIT: "FAN_ENABLE_AUDIT", 21 | } 22 | 23 | var fanotifyMarkFlags = map[int]string{ 24 | unix.FAN_MARK_ADD: "FAN_MARK_ADD", 25 | unix.FAN_MARK_REMOVE: "FAN_MARK_REMOVE", 26 | unix.FAN_MARK_DONT_FOLLOW: "FAN_MARK_DONT_FOLLOW", 27 | unix.FAN_MARK_ONLYDIR: "FAN_MARK_ONLYDIR", 28 | unix.FAN_MARK_MOUNT: "FAN_MARK_MOUNT", 29 | unix.FAN_MARK_IGNORED_MASK: "FAN_MARK_IGNORED_MASK", 30 | unix.FAN_MARK_IGNORED_SURV_MODIFY: "FAN_MARK_IGNORED_SURV_MODIFY", 31 | unix.FAN_MARK_FLUSH: "FAN_MARK_FLUSH", 32 | unix.FAN_MARK_FILESYSTEM: "FAN_MARK_FILESYSTEM", 33 | } 34 | 35 | func AnnotateFANotifyFlags(arg Arg, pid int) { 36 | var joins []string 37 | for flag, str := range fanotifyFlags { 38 | if int(arg.Raw())&flag != 0 { 39 | joins = append(joins, str) 40 | } 41 | } 42 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 43 | } 44 | 45 | func AnnotateFANotifyEventFlags(arg Arg, pid int) { 46 | AnnotateOpenFlags(arg, pid) 47 | } 48 | 49 | func AnnotateFANotifyMarkFlags(arg Arg, pid int) { 50 | var joins []string 51 | for flag, str := range fanotifyMarkFlags { 52 | if int(arg.Raw())&flag != 0 { 53 | joins = append(joins, str) 54 | } 55 | } 56 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 57 | } 58 | -------------------------------------------------------------------------------- /tracer/annotation/fcntl.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var fcntlCmds = map[int]string{ 10 | unix.F_DUPFD: "F_DUPFD", 11 | unix.F_DUPFD_CLOEXEC: "F_DUPFD_CLOEXEC", 12 | unix.F_GETFD: "F_GETFD", 13 | unix.F_SETFD: "F_SETFD", 14 | unix.F_GETFL: "F_GETFL", 15 | unix.F_SETFL: "F_SETFL", 16 | unix.F_GETLK: "F_GETLK", 17 | unix.F_SETLK: "F_SETLK", 18 | unix.F_SETLKW: "F_SETLKW", 19 | unix.F_SETOWN: "F_SETOWN", 20 | unix.F_GETOWN: "F_GETOWN", 21 | unix.F_GETOWN_EX: "F_GETOWN_EX", 22 | unix.F_SETOWN_EX: "F_SETOWN_EX", 23 | unix.F_GETSIG: "F_GETSIG", 24 | unix.F_SETSIG: "F_SETSIG", 25 | unix.F_GETLEASE: "F_GETLEASE", 26 | unix.F_SETLEASE: "F_SETLEASE", 27 | unix.F_NOTIFY: "F_NOTIFY", 28 | unix.F_GETPIPE_SZ: "F_GETPIPE_SZ", 29 | unix.F_SETPIPE_SZ: "F_SETPIPE_SZ", 30 | } 31 | 32 | func AnnotateFcntlCmd(arg Arg, pid int) { 33 | if str, ok := fcntlCmds[int(arg.Raw())]; ok { 34 | arg.SetAnnotation(str, true) 35 | } 36 | } 37 | 38 | var atFlags = map[int]string{ 39 | unix.AT_SYMLINK_NOFOLLOW: "AT_SYMLINK_NOFOLLOW", 40 | unix.AT_REMOVEDIR: "AT_REMOVEDIR", 41 | unix.AT_SYMLINK_FOLLOW: "AT_SYMLINK_FOLLOW", 42 | unix.AT_EMPTY_PATH: "AT_EMPTY_PATH", 43 | unix.AT_NO_AUTOMOUNT: "AT_NO_AUTOMOUNT", 44 | } 45 | 46 | func AnnotateAtFlags(arg Arg, pid int) { 47 | var joins []string 48 | for flag, str := range atFlags { 49 | if int(arg.Raw())&flag != 0 { 50 | joins = append(joins, str) 51 | } 52 | } 53 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 54 | } 55 | -------------------------------------------------------------------------------- /tracer/annotation/fd.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strconv" 7 | "strings" 8 | "syscall" 9 | 10 | "github.com/liamg/grace/tracer/netw" 11 | 12 | "golang.org/x/sys/unix" 13 | ) 14 | 15 | func AnnotateFd(arg Arg, pid int) { 16 | 17 | if path, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/%d", pid, arg.Raw())); err == nil { 18 | 19 | switch { 20 | case strings.HasPrefix(path, "socket:["): 21 | fd := strings.TrimPrefix(path, "socket:[") 22 | fd = strings.TrimSuffix(fd, "]") 23 | if inode, err := strconv.Atoi(fd); err == nil { 24 | if str := socketInoToString(inode); str != "" { 25 | arg.SetAnnotation(str, false) 26 | return 27 | } 28 | } 29 | } 30 | arg.SetAnnotation(path, false) 31 | } 32 | 33 | if int32(arg.Raw()) == unix.AT_FDCWD { 34 | arg.SetAnnotation("AT_FDCWD", true) 35 | return 36 | } 37 | 38 | switch int(arg.Raw()) { 39 | case syscall.Stdin: 40 | arg.SetAnnotation("STDIN", false) 41 | case syscall.Stdout: 42 | arg.SetAnnotation("STDOUT", false) 43 | case syscall.Stderr: 44 | arg.SetAnnotation("STDERR", false) 45 | } 46 | } 47 | 48 | func socketInoToString(inode int) string { 49 | if conns, err := netw.ListConnections(); err == nil { 50 | for _, conn := range conns { 51 | if conn.INode == inode { 52 | return fmt.Sprintf("%s %s:%d -> %s:%d", conn.Protocol, conn.LocalAddress, conn.LocalPort, conn.RemoteAddress, conn.RemotePort) 53 | } 54 | } 55 | } 56 | return "" 57 | } 58 | -------------------------------------------------------------------------------- /tracer/annotation/flock.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var flockOps = map[int]string{ 6 | unix.LOCK_SH: "LOCK_SH", 7 | unix.LOCK_EX: "LOCK_EX", 8 | unix.LOCK_NB: "LOCK_NB", 9 | unix.LOCK_UN: "LOCK_UN", 10 | } 11 | 12 | func AnnotateFlockOperation(arg Arg, _ int) { 13 | if str, ok := flockOps[int(arg.Raw())]; ok { 14 | arg.SetAnnotation(str, true) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tracer/annotation/futex.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | var futexOps = map[int]string{ 4 | 0: "FUTEX_WAIT", 5 | 1: "FUTEX_WAKE", 6 | 2: "FUTEX_FD", 7 | 3: "FUTEX_REQUEUE", 8 | 4: "FUTEX_CMP_REQUEUE", 9 | 5: "FUTEX_WAKE_OP", 10 | 6: "FUTEX_LOCK_PI", 11 | 7: "FUTEX_UNLOCK_PI", 12 | 8: "FUTEX_TRYLOCK_PI", 13 | 9: "FUTEX_WAIT_BITSET", 14 | 10: "FUTEX_WAKE_BITSET", 15 | 11: "FUTEX_WAIT_REQUEUE_PI", 16 | 12: "FUTEX_CMP_REQUEUE_PI", 17 | 128 | 0: "FUTEX_PRIVATE_FLAG|FUTEX_WAIT", 18 | 128 | 1: "FUTEX_PRIVATE_FLAG|FUTEX_WAKE", 19 | 128 | 2: "FUTEX_PRIVATE_FLAG|FUTEX_FD", 20 | 128 | 3: "FUTEX_PRIVATE_FLAG|FUTEX_REQUEUE", 21 | 128 | 4: "FUTEX_PRIVATE_FLAG|FUTEX_CMP_REQUEUE", 22 | 128 | 5: "FUTEX_PRIVATE_FLAG|FUTEX_WAKE_OP", 23 | 128 | 6: "FUTEX_PRIVATE_FLAG|FUTEX_LOCK_PI", 24 | 128 | 7: "FUTEX_PRIVATE_FLAG|FUTEX_UNLOCK_PI", 25 | 128 | 8: "FUTEX_PRIVATE_FLAG|FUTEX_TRYLOCK_PI", 26 | 128 | 9: "FUTEX_PRIVATE_FLAG|FUTEX_WAIT_BITSET", 27 | 128 | 10: "FUTEX_PRIVATE_FLAG|FUTEX_WAKE_BITSET", 28 | 128 | 11: "FUTEX_PRIVATE_FLAG|FUTEX_WAIT_REQUEUE_PI", 29 | 128 | 12: "FUTEX_PRIVATE_FLAG|FUTEX_CMP_REQUEUE_PI", 30 | 256 | 0: "FUTEX_CLOCK_REALTIME|FUTEX_WAIT", 31 | 256 | 1: "FUTEX_CLOCK_REALTIME|FUTEX_WAKE", 32 | 256 | 2: "FUTEX_CLOCK_REALTIME|FUTEX_FD", 33 | 256 | 3: "FUTEX_CLOCK_REALTIME|FUTEX_REQUEUE", 34 | 256 | 4: "FUTEX_CLOCK_REALTIME|FUTEX_CMP_REQUEUE", 35 | 256 | 5: "FUTEX_CLOCK_REALTIME|FUTEX_WAKE_OP", 36 | 256 | 6: "FUTEX_CLOCK_REALTIME|FUTEX_LOCK_PI", 37 | 256 | 7: "FUTEX_CLOCK_REALTIME|FUTEX_UNLOCK_PI", 38 | 256 | 8: "FUTEX_CLOCK_REALTIME|FUTEX_TRYLOCK_PI", 39 | 256 | 9: "FUTEX_CLOCK_REALTIME|FUTEX_WAIT_BITSET", 40 | 256 | 10: "FUTEX_CLOCK_REALTIME|FUTEX_WAKE_BITSET", 41 | 256 | 11: "FUTEX_CLOCK_REALTIME|FUTEX_WAIT_REQUEUE_PI", 42 | 256 | 12: "FUTEX_CLOCK_REALTIME|FUTEX_CMP_REQUEUE_PI", 43 | 384 | 0: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAIT", 44 | 384 | 1: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAKE", 45 | 384 | 2: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_FD", 46 | 384 | 3: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_REQUEUE", 47 | 384 | 4: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_CMP_REQUEUE", 48 | 384 | 5: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAKE_OP", 49 | 384 | 6: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_LOCK_PI", 50 | 384 | 7: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_UNLOCK_PI", 51 | 384 | 8: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_TRYLOCK_PI", 52 | 384 | 9: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAIT_BITSET", 53 | 384 | 10: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAKE_BITSET", 54 | 384 | 11: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_WAIT_REQUEUE_PI", 55 | 384 | 12: "FUTEX_CLOCK_REALTIME|FUTEX_PRIVATE_FLAG|FUTEX_CMP_REQUEUE_PI", 56 | } 57 | 58 | func AnnotateFutexOp(arg Arg, pid int) { 59 | if s, ok := futexOps[int(arg.Raw())]; ok { 60 | arg.SetAnnotation(s, true) 61 | } else { 62 | AnnotateHex(arg, pid) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /tracer/annotation/hex.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "fmt" 4 | 5 | func AnnotateHex(arg Arg, _ int) { 6 | arg.SetAnnotation(fmt.Sprintf("0x%x", arg.Raw()), true) 7 | } 8 | -------------------------------------------------------------------------------- /tracer/annotation/ioprio.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | var ioPrioWhiches = map[int]string{ 4 | 1: "IOPRIO_WHO_PROCESS", 5 | 2: "IOPRIO_WHO_PGRP", 6 | 3: "IOPRIO_WHO_USER", 7 | } 8 | 9 | func AnnotateIoPrioWhich(arg Arg, _ int) { 10 | if which, ok := ioPrioWhiches[int(arg.Raw())]; ok { 11 | arg.SetAnnotation(which, true) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tracer/annotation/ioring.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "strings" 4 | 5 | var ioringEnterFlags = map[int]string{ 6 | 1: "IORING_ENTER_GETEVENTS", 7 | 2: "IORING_ENTER_SQ_WAKEUP", 8 | 4: "IORING_ENTER_SQ_WAIT", 9 | 8: "IORING_ENTER_EXT_ARG", 10 | 16: "IORING_ENTER_REGISTERED_RING", 11 | } 12 | 13 | func AnnotateIoUringEnterFlags(arg Arg, pid int) { 14 | var joins []string 15 | for flag, name := range ioringEnterFlags { 16 | if int(arg.Raw())&flag != 0 { 17 | joins = append(joins, name) 18 | } 19 | } 20 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 21 | } 22 | 23 | var ioringOpCodes = []string{ 24 | "IORING_REGISTER_BUFFERS", 25 | "IORING_UNREGISTER_BUFFERS", 26 | "IORING_REGISTER_FILES", 27 | "IORING_UNREGISTER_FILES", 28 | "IORING_REGISTER_EVENTFD", 29 | "IORING_UNREGISTER_EVENTFD", 30 | "IORING_REGISTER_FILES_UPDATE", 31 | "IORING_REGISTER_EVENTFD_ASYNC", 32 | "IORING_REGISTER_PROBE", 33 | "IORING_REGISTER_PERSONALITY", 34 | "IORING_UNREGISTER_PERSONALITY", 35 | "IORING_REGISTER_RESTRICTIONS", 36 | "IORING_REGISTER_ENABLE_RINGS", 37 | "IORING_REGISTER_FILES2", 38 | "IORING_REGISTER_FILES_UPDATE2", 39 | "IORING_REGISTER_BUFFERS2", 40 | "IORING_REGISTER_BUFFERS_UPDATE", 41 | "IORING_REGISTER_IOWQ_AFF", 42 | "IORING_UNREGISTER_IOWQ_AFF", 43 | "IORING_REGISTER_IOWQ_MAX_WORKERS", 44 | "IORING_REGISTER_RING_FDS", 45 | "IORING_UNREGISTER_RING_FDS", 46 | "IORING_REGISTER_PBUF_RING", 47 | "IORING_UNREGISTER_PBUF_RING", 48 | "IORING_REGISTER_SYNC_CANCEL", 49 | "IORING_REGISTER_FILE_ALLOC_RANGE", 50 | } 51 | 52 | func AnnotateIORingOpCode(arg Arg, _ int) { 53 | opcode := int(arg.Raw()) 54 | if opcode >= 0 && opcode < len(ioringOpCodes) { 55 | arg.SetAnnotation(ioringOpCodes[opcode], true) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tracer/annotation/kcmp.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | var kcmpTypes = map[int]string{ 4 | 0: "KCMP_FILE", 5 | 1: "KCMP_VM", 6 | 2: "KCMP_FILES", 7 | 3: "KCMP_FS", 8 | 4: "KCMP_SIGHAND", 9 | 5: "KCMP_IO", 10 | 6: "KCMP_SYSVSEM", 11 | 7: "KCMP_TYPES", 12 | 8: "KCMP_EPOLL_TFD", 13 | } 14 | 15 | func AnnotateKcmpType(arg Arg, pid int) { 16 | if str, ok := kcmpTypes[int(arg.Raw())]; ok { 17 | arg.SetAnnotation(str, true) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tracer/annotation/kexec.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var kexecLoadFlags = map[int]string{ 10 | unix.KEXEC_FILE_UNLOAD: "KEXEC_FILE_UNLOAD", 11 | unix.KEXEC_FILE_ON_CRASH: "KEXEC_FILE_ON_CRASH", 12 | unix.KEXEC_FILE_NO_INITRAMFS: "KEXEC_FILE_NO_INITRAMFS", 13 | } 14 | 15 | func AnnotateKexecFlags(arg Arg, _ int) { 16 | var joins []string 17 | for flag, name := range kexecLoadFlags { 18 | if int(arg.Raw())&flag != 0 { 19 | joins = append(joins, name) 20 | } 21 | } 22 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 23 | } 24 | -------------------------------------------------------------------------------- /tracer/annotation/key.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var keyctlCmds = map[int]string{ 6 | unix.KEYCTL_GET_KEYRING_ID: "KEYCTL_GET_KEYRING_ID", 7 | unix.KEYCTL_JOIN_SESSION_KEYRING: "KEYCTL_JOIN_SESSION_KEYRING", 8 | unix.KEYCTL_UPDATE: "KEYCTL_UPDATE", 9 | unix.KEYCTL_REVOKE: "KEYCTL_REVOKE", 10 | unix.KEYCTL_DESCRIBE: "KEYCTL_DESCRIBE", 11 | unix.KEYCTL_CLEAR: "KEYCTL_CLEAR", 12 | unix.KEYCTL_LINK: "KEYCTL_LINK", 13 | unix.KEYCTL_UNLINK: "KEYCTL_UNLINK", 14 | unix.KEYCTL_SEARCH: "KEYCTL_SEARCH", 15 | unix.KEYCTL_READ: "KEYCTL_READ", 16 | unix.KEYCTL_CHOWN: "KEYCTL_CHOWN", 17 | unix.KEYCTL_SETPERM: "KEYCTL_SETPERM", 18 | unix.KEYCTL_INSTANTIATE: "KEYCTL_INSTANTIATE", 19 | unix.KEYCTL_NEGATE: "KEYCTL_NEGATE", 20 | unix.KEYCTL_SET_REQKEY_KEYRING: "KEYCTL_SET_REQKEY_KEYRING", 21 | unix.KEYCTL_SET_TIMEOUT: "KEYCTL_SET_TIMEOUT", 22 | unix.KEYCTL_ASSUME_AUTHORITY: "KEYCTL_ASSUME_AUTHORITY", 23 | unix.KEYCTL_GET_SECURITY: "KEYCTL_GET_SECURITY", 24 | unix.KEYCTL_SESSION_TO_PARENT: "KEYCTL_SESSION_TO_PARENT", 25 | unix.KEYCTL_REJECT: "KEYCTL_REJECT", 26 | unix.KEYCTL_INSTANTIATE_IOV: "KEYCTL_INSTANTIATE_IOV", 27 | unix.KEYCTL_INVALIDATE: "KEYCTL_INVALIDATE", 28 | } 29 | 30 | func AnnotateKeyctlCommand(arg Arg, pid int) { 31 | if cmd, ok := keyctlCmds[int(arg.Raw())]; ok { 32 | arg.SetAnnotation(cmd, true) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tracer/annotation/keyring.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var keyringIDs = map[int]string{ 6 | unix.KEY_SPEC_THREAD_KEYRING: "KEY_SPEC_THREAD_KEYRING", 7 | unix.KEY_SPEC_PROCESS_KEYRING: "KEY_SPEC_PROCESS_KEYRING", 8 | unix.KEY_SPEC_SESSION_KEYRING: "KEY_SPEC_SESSION_KEYRING", 9 | unix.KEY_SPEC_USER_KEYRING: "KEY_SPEC_USER_KEYRING", 10 | unix.KEY_SPEC_USER_SESSION_KEYRING: "KEY_SPEC_USER_SESSION_KEYRING", 11 | unix.KEY_SPEC_GROUP_KEYRING: "KEY_SPEC_GROUP_KEYRING", 12 | } 13 | 14 | func AnnotateKeyringID(arg Arg, _ int) { 15 | if id, ok := keyringIDs[int(arg.Raw())]; ok { 16 | arg.SetAnnotation(id, true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tracer/annotation/landlock.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var landlockFlags = map[int]string{ 10 | unix.LANDLOCK_CREATE_RULESET_VERSION: "LANDLOCK_CREATE_RULESET_VERSION", 11 | } 12 | 13 | func AnnotateLandlockFlags(arg Arg, _ int) { 14 | var joins []string 15 | for flag, str := range landlockFlags { 16 | if int(arg.Raw())&flag != 0 { 17 | joins = append(joins, str) 18 | } 19 | } 20 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 21 | } 22 | 23 | var landlockRuleTypes = map[int]string{ 24 | unix.LANDLOCK_RULE_PATH_BENEATH: "LANDLOCK_RULE_PATH_BENEATH", 25 | } 26 | 27 | func AnnotateLandlockRuleType(arg Arg, _ int) { 28 | var joins []string 29 | for flag, str := range landlockRuleTypes { 30 | if int(arg.Raw())&flag != 0 { 31 | joins = append(joins, str) 32 | } 33 | } 34 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 35 | } 36 | -------------------------------------------------------------------------------- /tracer/annotation/madvise.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func AnnotateMAdviseAdvice(arg Arg, _ int) { 10 | flags := map[int]string{ 11 | unix.MADV_NORMAL: "MADV_NORMAL", 12 | unix.MADV_RANDOM: "MADV_RANDOM", 13 | unix.MADV_SEQUENTIAL: "MADV_SEQUENTIAL", 14 | unix.MADV_WILLNEED: "MADV_WILLNEED", 15 | unix.MADV_DONTNEED: "MADV_DONTNEED", 16 | unix.MADV_REMOVE: "MADV_REMOVE", 17 | unix.MADV_DONTFORK: "MADV_DONTFORK", 18 | unix.MADV_DOFORK: "MADV_DOFORK", 19 | unix.MADV_MERGEABLE: "MADV_MERGEABLE", 20 | unix.MADV_UNMERGEABLE: "MADV_UNMERGEABLE", 21 | unix.MADV_HUGEPAGE: "MADV_HUGEPAGE", 22 | unix.MADV_NOHUGEPAGE: "MADV_NOHUGEPAGE", 23 | unix.MADV_DONTDUMP: "MADV_DONTDUMP", 24 | unix.MADV_DODUMP: "MADV_DODUMP", 25 | unix.MADV_HWPOISON: "MADV_HWPOISON", 26 | unix.MADV_COLD: "MADV_COLD", 27 | unix.MADV_PAGEOUT: "MADV_PAGEOUT", 28 | } 29 | var joins []string 30 | for flag, str := range flags { 31 | if int(arg.Raw())&flag != 0 { 32 | joins = append(joins, str) 33 | } 34 | } 35 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 36 | } 37 | -------------------------------------------------------------------------------- /tracer/annotation/mbind.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "C" 4 | 5 | var numaModeFlags = map[int]string{ 6 | 0: "MPOL_DEFAULT", 7 | 1: "MPOL_PREFERRED", 8 | 2: "MPOL_BIND", 9 | 3: "MPOL_INTERLEAVE", 10 | 4: "MPOL_LOCAL", 11 | 5: "MPOL_MAX", 12 | 6: "MPOL_F_STATIC_NODES", 13 | 7: "MPOL_F_RELATIVE_NODES", 14 | 8: "MPOL_MF_STRICT", 15 | 9: "MPOL_MF_MOVE", 16 | 10: "MPOL_MF_MOVE_ALL", 17 | } 18 | 19 | func AnnotateNumaModeFlag(arg Arg, pid int) { 20 | if str, ok := numaModeFlags[int(arg.Raw())]; ok { 21 | arg.SetAnnotation(str, true) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tracer/annotation/membarrier.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "strings" 4 | 5 | /* 6 | #include 7 | */ 8 | import "C" 9 | 10 | var membarrierCmds = map[int]string{ 11 | C.MEMBARRIER_CMD_QUERY: "MEMBARRIER_CMD_QUERY", 12 | C.MEMBARRIER_CMD_GLOBAL: "MEMBARRIER_CMD_GLOBAL", 13 | C.MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED: "MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED", 14 | C.MEMBARRIER_CMD_PRIVATE_EXPEDITED: "MEMBARRIER_CMD_PRIVATE_EXPEDITED", 15 | C.MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED: "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED", 16 | C.MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE: "MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE", 17 | C.MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE: "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE", 18 | } 19 | 20 | var membarrierCmdFlags = map[int]string{ 21 | 1: "MEMBARRIER_CMD_FLAG_CPU", 22 | } 23 | 24 | func AnnotateMembarrierCmd(arg Arg, _ int) { 25 | if name, ok := membarrierCmds[int(arg.Raw())]; ok { 26 | arg.SetAnnotation(name, true) 27 | } 28 | } 29 | 30 | func AnnotateMembarrierCmdFlags(arg Arg, _ int) { 31 | var joins []string 32 | for flag, name := range membarrierCmdFlags { 33 | if int(arg.Raw())&flag != 0 { 34 | joins = append(joins, name) 35 | } 36 | } 37 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 38 | } 39 | -------------------------------------------------------------------------------- /tracer/annotation/memfd.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var memfdFlags = map[int]string{ 10 | unix.MFD_CLOEXEC: "MFD_CLOEXEC", 11 | unix.MFD_ALLOW_SEALING: "MFD_ALLOW_SEALING", 12 | unix.MFD_HUGETLB: "MFD_HUGETLB", 13 | unix.MFD_HUGE_16GB: "MFD_HUGE_16GB", 14 | unix.MFD_HUGE_16MB: "MFD_HUGE_16MB", 15 | unix.MFD_HUGE_1GB: "MFD_HUGE_1GB", 16 | unix.MFD_HUGE_1MB: "MFD_HUGE_1MB", 17 | unix.MFD_HUGE_256MB: "MFD_HUGE_256MB", 18 | unix.MFD_HUGE_2GB: "MFD_HUGE_2GB", 19 | unix.MFD_HUGE_2MB: "MFD_HUGE_2MB", 20 | unix.MFD_HUGE_32MB: "MFD_HUGE_32MB", 21 | unix.MFD_HUGE_512KB: "MFD_HUGE_512KB", 22 | unix.MFD_HUGE_512MB: "MFD_HUGE_512MB", 23 | unix.MFD_HUGE_64KB: "MFD_HUGE_64KB", 24 | unix.MFD_HUGE_8MB: "MFD_HUGE_8MB", 25 | } 26 | 27 | func AnnotateMemfdFlags(arg Arg, pid int) { 28 | var joins []string 29 | for flag, name := range memfdFlags { 30 | if int(arg.Raw())&flag != 0 { 31 | joins = append(joins, name) 32 | } 33 | } 34 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 35 | } 36 | -------------------------------------------------------------------------------- /tracer/annotation/mlock.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var mlockFlags = map[int]string{ 10 | unix.MCL_CURRENT: "MCL_CURRENT", 11 | unix.MCL_FUTURE: "MCL_FUTURE", 12 | unix.MCL_ONFAULT: "MCL_ONFAULT", 13 | } 14 | 15 | func AnnotateMlockFlags(arg Arg, _ int) { 16 | var joins []string 17 | for flag, str := range mlockFlags { 18 | if int(arg.Raw())&flag != 0 { 19 | joins = append(joins, str) 20 | } 21 | } 22 | arg.SetAnnotation(strings.Join(joins, "|"), true) 23 | } 24 | -------------------------------------------------------------------------------- /tracer/annotation/mmap.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func AnnotateMMapFlags(arg Arg, _ int) { 10 | var joins []string 11 | 12 | switch arg.Raw() & 0x3 { 13 | case unix.MAP_SHARED: 14 | joins = append(joins, "MAP_SHARED") 15 | case unix.MAP_PRIVATE: 16 | joins = append(joins, "MAP_PRIVATE") 17 | case unix.MAP_SHARED_VALIDATE: 18 | joins = append(joins, "MAP_SHARED_VALIDATE") 19 | } 20 | 21 | mapConsts := map[int]string{ 22 | unix.MAP_32BIT: "MAP_32BIT", 23 | unix.MAP_ANONYMOUS: "MAP_ANONYMOUS", 24 | unix.MAP_DENYWRITE: "MAP_DENYWRITE", 25 | unix.MAP_EXECUTABLE: "MAP_EXECUTABLE", 26 | unix.MAP_FILE: "MAP_FILE", 27 | unix.MAP_FIXED: "MAP_FIXED", 28 | unix.MAP_FIXED_NOREPLACE: "MAP_FIXED_NOREPLACE", 29 | unix.MAP_GROWSDOWN: "MAP_GROWSDOWN", 30 | unix.MAP_HUGETLB: "MAP_HUGETLB", 31 | 21 << unix.MAP_HUGE_SHIFT: "MAP_HUGE_2MB", 32 | 30 << unix.MAP_HUGE_SHIFT: "MAP_HUGE_1GB", 33 | unix.MAP_LOCKED: "MAP_LOCKED", 34 | unix.MAP_NONBLOCK: "MAP_NONBLOCK", 35 | unix.MAP_NORESERVE: "MAP_NORESERVE", 36 | unix.MAP_POPULATE: "MAP_POPULATE", 37 | unix.MAP_STACK: "MAP_STACK", 38 | unix.MAP_SYNC: "MAP_SYNC", 39 | } 40 | 41 | for flag, str := range mapConsts { 42 | if arg.Raw()&uintptr(flag) > 0 { 43 | joins = append(joins, str) 44 | } 45 | } 46 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 47 | } 48 | -------------------------------------------------------------------------------- /tracer/annotation/module.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "C" 4 | import ( 5 | "strings" 6 | 7 | "golang.org/x/sys/unix" 8 | ) 9 | 10 | var deleteModuleFlags = map[int]string{ 11 | unix.O_NONBLOCK: "O_NONBLOCK", 12 | unix.O_TRUNC: "O_TRUNC", 13 | } 14 | 15 | func AnnotateDeleteModuleFlags(arg Arg, _ int) { 16 | var joins []string 17 | for flag, name := range deleteModuleFlags { 18 | if int(arg.Raw())&flag != 0 { 19 | joins = append(joins, name) 20 | } 21 | } 22 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 23 | } 24 | 25 | var queryModuleWhiches = map[int]string{ 26 | 1: "QM_MODULES", 27 | 2: "QM_DEPS", 28 | 3: "QM_REFS", 29 | 4: "QM_SYMBOLS", 30 | 5: "QM_INFO", 31 | 6: "QM_EXPORTS", 32 | } 33 | 34 | func AnnotateQueryModuleWhich(arg Arg, _ int) { 35 | if name, ok := queryModuleWhiches[int(arg.Raw())]; ok { 36 | arg.SetAnnotation(name, true) 37 | } 38 | } 39 | 40 | var moduleInitFlags = map[int]string{ 41 | unix.MODULE_INIT_IGNORE_MODVERSIONS: "MODULE_INIT_IGNORE_MODVERSIONS", 42 | unix.MODULE_INIT_IGNORE_VERMAGIC: "MODULE_INIT_IGNORE_VERMAGIC", 43 | } 44 | 45 | func AnnotateModuleInitFlags(arg Arg, _ int) { 46 | var joins []string 47 | for flag, name := range moduleInitFlags { 48 | if int(arg.Raw())&flag != 0 { 49 | joins = append(joins, name) 50 | } 51 | } 52 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 53 | } 54 | -------------------------------------------------------------------------------- /tracer/annotation/mount.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var mountFlags = map[int]string{ 10 | unix.MS_BIND: "MS_BIND", 11 | unix.MS_DIRSYNC: "MS_DIRSYNC", 12 | unix.MS_MANDLOCK: "MS_MANDLOCK", 13 | unix.MS_MOVE: "MS_MOVE", 14 | unix.MS_NOATIME: "MS_NOATIME", 15 | unix.MS_NODEV: "MS_NODEV", 16 | unix.MS_NODIRATIME: "MS_NODIRATIME", 17 | unix.MS_NOEXEC: "MS_NOEXEC", 18 | unix.MS_NOSUID: "MS_NOSUID", 19 | unix.MS_RDONLY: "MS_RDONLY", 20 | unix.MS_REC: "MS_REC", 21 | unix.MS_RELATIME: "MS_RELATIME", 22 | unix.MS_REMOUNT: "MS_REMOUNT", 23 | unix.MS_SILENT: "MS_SILENT", 24 | unix.MS_STRICTATIME: "MS_STRICTATIME", 25 | unix.MS_SYNCHRONOUS: "MS_SYNCHRONOUS", 26 | } 27 | 28 | func AnnotateMountFlags(arg Arg, _ int) { 29 | var joins []string 30 | for flag, name := range mountFlags { 31 | if int(arg.Raw())&flag != 0 { 32 | joins = append(joins, name) 33 | } 34 | } 35 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 36 | } 37 | 38 | var umountFlags = map[int]string{ 39 | unix.MNT_FORCE: "MNT_FORCE", 40 | unix.MNT_DETACH: "MNT_DETACH", 41 | unix.MNT_EXPIRE: "MNT_EXPIRE", 42 | unix.UMOUNT_NOFOLLOW: "UMOUNT_NOFOLLOW", 43 | } 44 | 45 | func AnnotateUmountFlags(arg Arg, _ int) { 46 | var joins []string 47 | for flag, name := range umountFlags { 48 | if int(arg.Raw())&flag != 0 { 49 | joins = append(joins, name) 50 | } 51 | } 52 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 53 | } 54 | -------------------------------------------------------------------------------- /tracer/annotation/mremap.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "strings" 4 | 5 | // see https://github.com/torvalds/linux/blob/master/include/uapi/linux/mman.h 6 | func AnnotateMRemapFlags(arg Arg, _ int) { 7 | flags := map[int]string{ 8 | 1: "MREMAP_MAYMOVE", 9 | 2: "MREMAP_FIXED", 10 | 4: "MREMAP_DONTUNMAP", 11 | } 12 | var joins []string 13 | for flag, str := range flags { 14 | if int(arg.Raw())&flag != 0 { 15 | joins = append(joins, str) 16 | } 17 | } 18 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 19 | } 20 | -------------------------------------------------------------------------------- /tracer/annotation/msg.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var msgFlags = map[int]string{ 10 | unix.MSG_OOB: "MSG_OOB", 11 | unix.MSG_PEEK: "MSG_PEEK", 12 | unix.MSG_DONTROUTE: "MSG_DONTROUTE", 13 | unix.MSG_CTRUNC: "MSG_CTRUNC", 14 | unix.MSG_PROXY: "MSG_PROXY", 15 | unix.MSG_TRUNC: "MSG_TRUNC", 16 | unix.MSG_DONTWAIT: "MSG_DONTWAIT", 17 | unix.MSG_EOR: "MSG_EOR", 18 | unix.MSG_WAITALL: "MSG_WAITALL", 19 | unix.MSG_FIN: "MSG_FIN", 20 | unix.MSG_SYN: "MSG_SYN", 21 | unix.MSG_CONFIRM: "MSG_CONFIRM", 22 | unix.MSG_RST: "MSG_RST", 23 | unix.MSG_ERRQUEUE: "MSG_ERRQUEUE", 24 | unix.MSG_NOSIGNAL: "MSG_NOSIGNAL", 25 | unix.MSG_MORE: "MSG_MORE", 26 | unix.MSG_WAITFORONE: "MSG_WAITFORONE", 27 | unix.MSG_FASTOPEN: "MSG_FASTOPEN", 28 | unix.MSG_CMSG_CLOEXEC: "MSG_CMSG_CLOEXEC", 29 | } 30 | 31 | func AnnotateMsgFlags(arg Arg, _ int) { 32 | var joins []string 33 | for flag, str := range msgFlags { 34 | if arg.Raw()&uintptr(flag) != 0 { 35 | joins = append(joins, str) 36 | } 37 | } 38 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 39 | } 40 | 41 | func AnnotateMsgType(arg Arg, pid int) { 42 | // TODO: 43 | } 44 | -------------------------------------------------------------------------------- /tracer/annotation/msync.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var mSyncFlags = map[int]string{ 10 | unix.MS_ASYNC: "MS_ASYNC", 11 | unix.MS_INVALIDATE: "MS_INVALIDATE", 12 | unix.MS_SYNC: "MS_SYNC", 13 | } 14 | 15 | func AnnotateMSyncFlags(arg Arg, _ int) { 16 | var joins []string 17 | for flag, str := range mSyncFlags { 18 | if int(arg.Raw())&flag != 0 { 19 | joins = append(joins, str) 20 | } 21 | } 22 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 23 | } 24 | -------------------------------------------------------------------------------- /tracer/annotation/null.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | func AnnotateNull(arg Arg, _ int) { 4 | if arg.Raw() == 0 { 5 | arg.SetAnnotation("NULL", true) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tracer/annotation/open.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var openFlags = map[int]string{ 10 | unix.O_APPEND: "O_APPEND", 11 | unix.O_ASYNC: "O_ASYNC", 12 | unix.O_CLOEXEC: "O_CLOEXEC", 13 | unix.O_CREAT: "O_CREAT", 14 | unix.O_DIRECT: "O_DIRECT", 15 | unix.O_DIRECTORY: "O_DIRECTORY", 16 | unix.O_DSYNC: "O_DSYNC", 17 | unix.O_EXCL: "O_EXCL", 18 | unix.O_NOATIME: "O_NOATIME", 19 | unix.O_NOCTTY: "O_NOCTTY", 20 | unix.O_NOFOLLOW: "O_NOFOLLOW", 21 | unix.O_NONBLOCK: "O_NONBLOCK", 22 | unix.O_PATH: "O_PATH", 23 | unix.O_SYNC: "O_SYNC", 24 | unix.O_TMPFILE: "O_TMPFILE", 25 | unix.O_TRUNC: "O_TRUNC", 26 | unix.O_RDONLY: "O_RDONLY", 27 | unix.O_WRONLY: "O_WRONLY", 28 | unix.O_RDWR: "O_RDWR", 29 | } 30 | 31 | func AnnotateOpenFlags(arg Arg, _ int) { 32 | var joins []string 33 | for flag, name := range openFlags { 34 | if (int(arg.Raw())&flag != 0) || (int(arg.Raw()) == flag) { 35 | joins = append(joins, name) 36 | } 37 | } 38 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 39 | } 40 | 41 | var resolveFlags = map[int]string{ 42 | unix.RESOLVE_BENEATH: "RESOLVE_BENEATH", 43 | unix.RESOLVE_IN_ROOT: "RESOLVE_IN_ROOT", 44 | unix.RESOLVE_NO_XDEV: "RESOLVE_NO_XDEV", 45 | unix.RESOLVE_NO_MAGICLINKS: "RESOLVE_NO_MAGICLINKS", 46 | unix.RESOLVE_NO_SYMLINKS: "RESOLVE_NO_SYMLINKS", 47 | // RESOLVE_CACHED is not available... 48 | } 49 | 50 | func AnnotateResolveFlags(arg Arg, _ int) { 51 | var joins []string 52 | for flag, name := range resolveFlags { 53 | if int(arg.Raw())&flag != 0 { 54 | joins = append(joins, name) 55 | } 56 | } 57 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 58 | } 59 | -------------------------------------------------------------------------------- /tracer/annotation/p2.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var p2Flags = map[int]string{ 10 | unix.RWF_APPEND: "RWF_APPEND", 11 | unix.RWF_DSYNC: "RWF_DSYNC", 12 | unix.RWF_HIPRI: "RWF_HIPRI", 13 | unix.RWF_NOWAIT: "RWF_NOWAIT", 14 | unix.RWF_SYNC: "RWF_SYNC", 15 | } 16 | 17 | func AnnotatePReadWrite2Flags(arg Arg, _ int) { 18 | var joins []string 19 | for flag, str := range p2Flags { 20 | if int(arg.Raw())&flag != 0 { 21 | joins = append(joins, str) 22 | } 23 | } 24 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 25 | } 26 | -------------------------------------------------------------------------------- /tracer/annotation/perf.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var perfFlags = map[int]string{ 10 | unix.PERF_FLAG_FD_CLOEXEC: "PERF_FLAG_FD_CLOEXEC", 11 | unix.PERF_FLAG_FD_OUTPUT: "PERF_FLAG_FD_OUTPUT", 12 | unix.PERF_FLAG_FD_NO_GROUP: "PERF_FLAG_FD_NO_GROUP", 13 | unix.PERF_FLAG_PID_CGROUP: "PERF_FLAG_PID_CGROUP", 14 | } 15 | 16 | func AnnotatePerfFlags(arg Arg, _ int) { 17 | var joins []string 18 | for flag, str := range perfFlags { 19 | if int(arg.Raw())&flag != 0 { 20 | joins = append(joins, str) 21 | } 22 | } 23 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 24 | } 25 | -------------------------------------------------------------------------------- /tracer/annotation/pkey.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | var pkeyAccessRights = map[int]string{ 4 | 1: "PKEY_DISABLE_ACCESS", 5 | 2: "PKEY_DISABLE_WRITE", 6 | } 7 | 8 | func AnnotatePkeyAccessRights(arg Arg, _ int) { 9 | if name, ok := pkeyAccessRights[int(arg.Raw())]; ok { 10 | arg.SetAnnotation(name, true) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tracer/annotation/prctl.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var prctlOptions = map[int]string{ 6 | unix.PR_CAP_AMBIENT: "PR_CAP_AMBIENT", 7 | unix.PR_CAP_AMBIENT_RAISE: "PR_CAP_AMBIENT_RAISE", 8 | unix.PR_CAP_AMBIENT_LOWER: "PR_CAP_AMBIENT_LOWER", 9 | unix.PR_CAP_AMBIENT_IS_SET: "PR_CAP_AMBIENT_IS_SET", 10 | unix.PR_CAP_AMBIENT_CLEAR_ALL: "PR_CAP_AMBIENT_CLEAR_ALL", 11 | unix.PR_CAPBSET_READ: "PR_CAPBSET_READ", 12 | unix.PR_CAPBSET_DROP: "PR_CAPBSET_DROP", 13 | unix.PR_SET_CHILD_SUBREAPER: "PR_SET_CHILD_SUBREAPER", 14 | unix.PR_GET_CHILD_SUBREAPER: "PR_GET_CHILD_SUBREAPER", 15 | unix.PR_SET_ENDIAN: "PR_SET_ENDIAN", 16 | unix.PR_GET_ENDIAN: "PR_GET_ENDIAN", 17 | unix.PR_SET_FPEMU: "PR_SET_FPEMU", 18 | unix.PR_GET_FPEMU: "PR_GET_FPEMU", 19 | unix.PR_SET_FPEXC: "PR_SET_FPEXC", 20 | unix.PR_GET_FPEXC: "PR_GET_FPEXC", 21 | unix.PR_SET_FP_MODE: "PR_SET_FP_MODE", 22 | unix.PR_GET_FP_MODE: "PR_GET_FP_MODE", 23 | unix.PR_MPX_ENABLE_MANAGEMENT: "PR_MPX_ENABLE_MANAGEMENT", 24 | unix.PR_MPX_DISABLE_MANAGEMENT: "PR_MPX_DISABLE_MANAGEMENT", 25 | unix.PR_SET_KEEPCAPS: "PR_SET_KEEPCAPS", 26 | unix.PR_GET_KEEPCAPS: "PR_GET_KEEPCAPS", 27 | unix.PR_MCE_KILL: "PR_MCE_KILL", 28 | unix.PR_MCE_KILL_GET: "PR_MCE_KILL_GET", 29 | unix.PR_SET_MM: "PR_SET_MM", 30 | unix.PR_SET_MM_START_STACK: "PR_SET_MM_START_STACK", 31 | unix.PR_SET_MM_START_BRK: "PR_SET_MM_START_BRK", 32 | unix.PR_SET_MM_EXE_FILE: "PR_SET_MM_EXE_FILE", 33 | unix.PR_SET_MM_MAP: "PR_SET_MM_MAP", 34 | unix.PR_SET_NAME: "PR_SET_NAME", 35 | unix.PR_GET_NAME: "PR_GET_NAME", 36 | unix.PR_SET_NO_NEW_PRIVS: "PR_SET_NO_NEW_PRIVS", 37 | unix.PR_GET_NO_NEW_PRIVS: "PR_GET_NO_NEW_PRIVS", 38 | unix.PR_SET_PTRACER: "PR_SET_PTRACER", 39 | unix.PR_SET_SECCOMP: "PR_SET_SECCOMP", 40 | unix.PR_GET_SECCOMP: "PR_GET_SECCOMP", 41 | unix.PR_SET_SECUREBITS: "PR_SET_SECUREBITS", 42 | unix.PR_GET_SECUREBITS: "PR_GET_SECUREBITS", 43 | unix.PR_SET_THP_DISABLE: "PR_SET_THP_DISABLE", 44 | unix.PR_TASK_PERF_EVENTS_DISABLE: "PR_TASK_PERF_EVENTS_DISABLE", 45 | unix.PR_TASK_PERF_EVENTS_ENABLE: "PR_TASK_PERF_EVENTS_ENABLE", 46 | unix.PR_GET_THP_DISABLE: "PR_GET_THP_DISABLE", 47 | unix.PR_GET_TID_ADDRESS: "PR_GET_TID_ADDRESS", 48 | unix.PR_SET_TIMERSLACK: "PR_SET_TIMERSLACK", 49 | unix.PR_GET_TIMERSLACK: "PR_GET_TIMERSLACK", 50 | unix.PR_SET_TSC: "PR_SET_TSC", 51 | unix.PR_GET_TSC: "PR_GET_TSC", 52 | unix.PR_MCE_KILL_CLEAR: "PR_MCE_KILL_CLEAR", 53 | unix.PR_SET_VMA: "PR_SET_VMA", 54 | } 55 | 56 | func AnnotatePrctlOption(arg Arg, _ int) { 57 | if s, ok := prctlOptions[int(arg.Raw())]; ok { 58 | arg.SetAnnotation(s, true) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /tracer/annotation/priority.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var priorityWhiches = map[int]string{ 6 | unix.PRIO_PROCESS: "PRIO_PROCESS", 7 | unix.PRIO_PGRP: "PRIO_PGRP", 8 | unix.PRIO_USER: "PRIO_USER", 9 | } 10 | 11 | func AnnotatePriorityWhich(arg Arg, _ int) { 12 | if s, ok := priorityWhiches[int(arg.Raw())]; ok { 13 | arg.SetAnnotation(s, true) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tracer/annotation/prot.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func AnnotateProt(arg Arg, _ int) { 10 | if arg.Raw() == unix.PROT_NONE { 11 | arg.SetAnnotation("PROT_NONE", true) 12 | return 13 | } 14 | var joins []string 15 | if arg.Raw()&unix.PROT_READ > 0 { 16 | joins = append(joins, "PROT_READ") 17 | } 18 | if arg.Raw()&unix.PROT_WRITE > 0 { 19 | joins = append(joins, "PROT_WRITE") 20 | } 21 | if arg.Raw()&unix.PROT_EXEC > 0 { 22 | joins = append(joins, "PROT_EXEC") 23 | } 24 | if arg.Raw()&unix.PROT_GROWSUP > 0 { 25 | joins = append(joins, "PROT_GROWSUP") 26 | } 27 | if arg.Raw()&unix.PROT_GROWSDOWN > 0 { 28 | joins = append(joins, "PROT_GROWSDOWN") 29 | } 30 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 31 | } 32 | -------------------------------------------------------------------------------- /tracer/annotation/ptrace.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var ptraceRequests = map[int]string{ 6 | unix.PTRACE_TRACEME: "PTRACE_TRACEME", 7 | unix.PTRACE_PEEKTEXT: "PTRACE_PEEKTEXT", 8 | unix.PTRACE_PEEKDATA: "PTRACE_PEEKDATA", 9 | unix.PTRACE_PEEKUSR: "PTRACE_PEEKUSR", 10 | unix.PTRACE_POKETEXT: "PTRACE_POKETEXT", 11 | unix.PTRACE_POKEDATA: "PTRACE_POKEDATA", 12 | unix.PTRACE_POKEUSR: "PTRACE_POKEUSR", 13 | unix.PTRACE_CONT: "PTRACE_CONT", 14 | unix.PTRACE_KILL: "PTRACE_KILL", 15 | unix.PTRACE_SINGLESTEP: "PTRACE_SINGLESTEP", 16 | unix.PTRACE_GETREGS: "PTRACE_GETREGS", 17 | unix.PTRACE_SETREGS: "PTRACE_SETREGS", 18 | unix.PTRACE_GETFPREGS: "PTRACE_GETFPREGS", 19 | unix.PTRACE_SETFPREGS: "PTRACE_SETFPREGS", 20 | unix.PTRACE_ATTACH: "PTRACE_ATTACH", 21 | unix.PTRACE_DETACH: "PTRACE_DETACH", 22 | unix.PTRACE_GETFPXREGS: "PTRACE_GETFPXREGS", 23 | unix.PTRACE_SETFPXREGS: "PTRACE_SETFPXREGS", 24 | unix.PTRACE_SYSCALL: "PTRACE_SYSCALL", 25 | unix.PTRACE_SETOPTIONS: "PTRACE_SETOPTIONS", 26 | unix.PTRACE_GETEVENTMSG: "PTRACE_GETEVENTMSG", 27 | unix.PTRACE_GETSIGINFO: "PTRACE_GETSIGINFO", 28 | unix.PTRACE_SETSIGINFO: "PTRACE_SETSIGINFO", 29 | unix.PTRACE_GETREGSET: "PTRACE_GETREGSET", 30 | unix.PTRACE_SETREGSET: "PTRACE_SETREGSET", 31 | unix.PTRACE_SEIZE: "PTRACE_SEIZE", 32 | unix.PTRACE_INTERRUPT: "PTRACE_INTERRUPT", 33 | unix.PTRACE_LISTEN: "PTRACE_LISTEN", 34 | unix.PTRACE_PEEKSIGINFO: "PTRACE_PEEKSIGINFO", 35 | unix.PTRACE_GETSIGMASK: "PTRACE_GETSIGMASK", 36 | unix.PTRACE_SETSIGMASK: "PTRACE_SETSIGMASK", 37 | } 38 | 39 | func AnnotatePtraceRequest(arg Arg, _ int) { 40 | if name, ok := ptraceRequests[int(arg.Raw())]; ok { 41 | arg.SetAnnotation(name, true) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tracer/annotation/quotactl.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | 8 | var quotactlCmds = map[uintptr]string{ 9 | C.Q_QUOTAON: "Q_QUOTAON", 10 | C.Q_QUOTAOFF: "Q_QUOTAOFF", 11 | } 12 | 13 | func AnnotateQuotactlCmd(arg Arg, _ int) { 14 | if cmd, ok := quotactlCmds[arg.Raw()]; ok { 15 | arg.SetAnnotation(cmd, true) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tracer/annotation/random.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var randomFlags = map[int]string{ 10 | unix.GRND_RANDOM: "GRND_RANDOM", 11 | unix.GRND_NONBLOCK: "GRND_NONBLOCK", 12 | } 13 | 14 | func AnnotateRandomFlags(arg Arg, _ int) { 15 | var joins []string 16 | for flag, name := range randomFlags { 17 | if int(arg.Raw())&flag != 0 { 18 | joins = append(joins, name) 19 | } 20 | } 21 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 22 | } 23 | -------------------------------------------------------------------------------- /tracer/annotation/reboot.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "C" 4 | 5 | // from reboot(2) manpages 6 | var rebootMagics = map[uintptr]string{ 7 | 0xfee1dead: "LINUX_REBOOT_MAGIC1", 8 | 672274793: "LINUX_REBOOT_MAGIC2", 9 | 85072278: "LINUX_REBOOT_MAGIC2A", 10 | 369367448: "LINUX_REBOOT_MAGIC2B", 11 | 537993216: "LINUX_REBOOT_MAGIC2C", 12 | } 13 | 14 | func AnnotateRebootMagic(arg Arg, _ int) { 15 | if magic, ok := rebootMagics[arg.Raw()]; ok { 16 | arg.SetAnnotation(magic, true) 17 | } 18 | } 19 | 20 | var rebootCmds = map[uintptr]string{ 21 | 0x00000000: "LINUX_REBOOT_CMD_CAD_OFF", 22 | 0x89ABCDEF: "LINUX_REBOOT_CMD_CAD_ON", 23 | 0xCDEF0123: "LINUX_REBOOT_CMD_HALT", 24 | 0x45584543: "LINUX_REBOOT_CMD_KEXEC", 25 | 0x4321FEDC: "LINUX_REBOOT_CMD_POWER_OFF", 26 | 0x01234567: "LINUX_REBOOT_CMD_RESTART", 27 | 0xA1B2C3D4: "LINUX_REBOOT_CMD_RESTART2", 28 | 0xD000FCE2: "LINUX_REBOOT_CMD_SW_SUSPEND", 29 | } 30 | 31 | func AnnotateRebootCmd(arg Arg, _ int) { 32 | if cmd, ok := rebootCmds[arg.Raw()]; ok { 33 | arg.SetAnnotation(cmd, true) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tracer/annotation/rlimit.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "golang.org/x/sys/unix" 5 | ) 6 | 7 | var rlimitFlags = map[int]string{ 8 | unix.RLIMIT_AS: "RLIMIT_AS", 9 | unix.RLIMIT_CORE: "RLIMIT_CORE", 10 | unix.RLIMIT_CPU: "RLIMIT_CPU", 11 | unix.RLIMIT_DATA: "RLIMIT_DATA", 12 | unix.RLIMIT_FSIZE: "RLIMIT_FSIZE", 13 | unix.RLIMIT_LOCKS: "RLIMIT_LOCKS", 14 | unix.RLIMIT_MEMLOCK: "RLIMIT_MEMLOCK", 15 | unix.RLIMIT_MSGQUEUE: "RLIMIT_MSGQUEUE", 16 | unix.RLIMIT_NICE: "RLIMIT_NICE", 17 | unix.RLIMIT_NOFILE: "RLIMIT_NOFILE", 18 | unix.RLIMIT_NPROC: "RLIMIT_NPROC", 19 | unix.RLIMIT_RSS: "RLIMIT_RSS", 20 | unix.RLIMIT_RTPRIO: "RLIMIT_RTPRIO", 21 | unix.RLIMIT_RTTIME: "RLIMIT_RTTIME", 22 | unix.RLIMIT_SIGPENDING: "RLIMIT_SIGPENDING", 23 | unix.RLIMIT_STACK: "RLIMIT_STACK", 24 | } 25 | 26 | func AnnotateRLimitResourceFlags(arg Arg, pid int) { 27 | if str, ok := rlimitFlags[int(arg.Raw())]; ok { 28 | arg.SetAnnotation(str, true) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tracer/annotation/rusage.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var rusageWho = map[int]string{ 6 | unix.RUSAGE_CHILDREN: "RUSAGE_CHILDREN", 7 | unix.RUSAGE_SELF: "RUSAGE_SELF", 8 | unix.RUSAGE_THREAD: "RUSAGE_THREAD", 9 | } 10 | 11 | func AnnotateRUsageWho(arg Arg, _ int) { 12 | if name, ok := rusageWho[int(arg.Raw())]; ok { 13 | arg.SetAnnotation(name, true) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tracer/annotation/sched.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | 8 | var schedPolicies = map[int]string{ 9 | C.SCHED_NORMAL: "SCHED_NORMAL", 10 | C.SCHED_FIFO: "SCHED_FIFO", 11 | C.SCHED_RR: "SCHED_RR", 12 | C.SCHED_BATCH: "SCHED_BATCH", 13 | C.SCHED_IDLE: "SCHED_IDLE", 14 | } 15 | 16 | func AnnotateSchedPolicy(arg Arg, pid int) { 17 | if s, ok := schedPolicies[int(arg.Raw())]; ok { 18 | arg.SetAnnotation(s, true) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tracer/annotation/seccomp.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | 8 | var seccompOps = map[int]string{ 9 | C.SECCOMP_SET_MODE_STRICT: "SECCOMP_SET_MODE_STRICT", 10 | C.SECCOMP_SET_MODE_FILTER: "SECCOMP_SET_MODE_FILTER", 11 | C.SECCOMP_GET_ACTION_AVAIL: "SECCOMP_GET_ACTION_AVAIL", 12 | } 13 | 14 | func AnnotateSeccompOp(arg Arg, pid int) { 15 | if s, ok := seccompOps[int(arg.Raw())]; ok { 16 | arg.SetAnnotation(s, true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tracer/annotation/sem.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | #include 6 | */ 7 | import "C" 8 | 9 | import ( 10 | "strings" 11 | 12 | "golang.org/x/sys/unix" 13 | ) 14 | 15 | var semFlags = map[int]string{ 16 | unix.IPC_CREAT: "IPC_CREAT", 17 | unix.IPC_EXCL: "IPC_EXCL", 18 | unix.IPC_NOWAIT: "IPC_NOWAIT", 19 | unix.S_IRUSR: "S_IRUSR", 20 | unix.S_IWUSR: "S_IWUSR", 21 | unix.S_IXUSR: "S_IXUSR", 22 | unix.S_IRGRP: "S_IRGRP", 23 | unix.S_IWGRP: "S_IWGRP", 24 | unix.S_IXGRP: "S_IXGRP", 25 | unix.S_IROTH: "S_IROTH", 26 | unix.S_IWOTH: "S_IWOTH", 27 | unix.S_IXOTH: "S_IXOTH", 28 | } 29 | 30 | func AnnotateSemFlags(arg Arg, pid int) { 31 | var joins []string 32 | for flag, name := range semFlags { 33 | if arg.Raw()&uintptr(flag) != 0 { 34 | joins = append(joins, name) 35 | } 36 | } 37 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 38 | } 39 | 40 | var semCmds = map[int]string{ 41 | unix.IPC_STAT: "IPC_STAT", 42 | unix.IPC_SET: "IPC_SET", 43 | unix.IPC_RMID: "IPC_RMID", 44 | C.IPC_INFO: "IPC_INFO", 45 | C.SEM_INFO: "SEM_INFO", 46 | C.SEM_STAT: "SEM_STAT", 47 | C.SEM_STAT_ANY: "SEM_STAT_ANY", 48 | C.GETALL: "GETALL", 49 | C.GETNCNT: "GETNCNT", 50 | C.GETPID: "GETPID", 51 | C.GETVAL: "GETVAL", 52 | C.GETZCNT: "GETZCNT", 53 | C.SETALL: "SETALL", 54 | C.SETVAL: "SETVAL", 55 | } 56 | 57 | func AnnotateSemCmd(arg Arg, _ int) { 58 | if name, ok := semCmds[int(arg.Raw())]; ok { 59 | arg.SetAnnotation(name, true) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tracer/annotation/shm.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | import ( 8 | "strings" 9 | 10 | "golang.org/x/sys/unix" 11 | ) 12 | 13 | var shmCommands = map[int]string{ 14 | unix.IPC_STAT: "IPC_STAT", 15 | unix.IPC_SET: "IPC_SET", 16 | unix.IPC_RMID: "IPC_RMID", 17 | } 18 | 19 | func AnnotateSHMCTLCommand(arg Arg, _ int) { 20 | str, ok := shmCommands[int(arg.Raw())] 21 | arg.SetAnnotation(str, ok) 22 | } 23 | 24 | var shmAtFlags = map[int]string{ 25 | unix.SHM_RDONLY: "SHM_RDONLY", 26 | C.SHM_REMAP: "SHM_REMAP", 27 | } 28 | 29 | func AnnotateSHMAtFlags(arg Arg, _ int) { 30 | var joins []string 31 | for flag, str := range shmAtFlags { 32 | if int(arg.Raw())&flag != 0 { 33 | joins = append(joins, str) 34 | } 35 | } 36 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 37 | } 38 | 39 | var shmGetFlags = map[int]string{ 40 | unix.IPC_CREAT: "IPC_CREAT", 41 | unix.IPC_EXCL: "IPC_EXCL", 42 | C.SHM_HUGETLB: "SHM_HUGETLB", 43 | C.SHM_HUGE_1GB: "SHM_HUGE_1GB", 44 | C.SHM_HUGE_2MB: "SHM_HUGE_2MB", 45 | C.SHM_NORESERVE: "SHM_NO_RESERVE", 46 | } 47 | 48 | func AnnotateSHMGetFlags(arg Arg, _ int) { 49 | var joins []string 50 | for flag, name := range shmGetFlags { 51 | if arg.Raw()&uintptr(flag) != 0 { 52 | joins = append(joins, name) 53 | } 54 | } 55 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 56 | } 57 | -------------------------------------------------------------------------------- /tracer/annotation/shutdown.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var shutdownHow = map[int]string{ 6 | unix.SHUT_RD: "SHUT_RD", 7 | unix.SHUT_WR: "SHUT_WR", 8 | unix.SHUT_RDWR: "SHUT_RDWR", 9 | } 10 | 11 | func AnnotateShutdownHow(arg Arg, _ int) { 12 | str, ok := shutdownHow[int(arg.Raw())] 13 | arg.SetAnnotation(str, ok) 14 | } 15 | -------------------------------------------------------------------------------- /tracer/annotation/signal.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | #include 6 | 7 | */ 8 | import "C" 9 | import ( 10 | "fmt" 11 | "strings" 12 | "syscall" 13 | 14 | "golang.org/x/sys/unix" 15 | ) 16 | 17 | func AnnotateSigProcMaskFlags(arg Arg, _ int) { 18 | var joins []string 19 | if arg.Raw()&C.SIG_BLOCK != 0 { 20 | joins = append(joins, "SIG_BLOCK") 21 | } 22 | if arg.Raw()&C.SIG_UNBLOCK != 0 { 23 | joins = append(joins, "SIG_UNBLOCK") 24 | } 25 | if arg.Raw()&C.SIG_SETMASK != 0 { 26 | joins = append(joins, "SIG_SETMASK") 27 | } 28 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 29 | } 30 | 31 | var signals = map[int]string{ 32 | C.SIGHUP: "SIGHUP", 33 | C.SIGINT: "SIGINT", 34 | C.SIGQUIT: "SIGQUIT", 35 | C.SIGILL: "SIGILL", 36 | C.SIGTRAP: "SIGTRAP", 37 | C.SIGABRT: "SIGABRT", 38 | C.SIGBUS: "SIGBUS", 39 | C.SIGFPE: "SIGFPE", 40 | C.SIGKILL: "SIGKILL", 41 | C.SIGUSR1: "SIGUSR1", 42 | C.SIGSEGV: "SIGSEGV", 43 | C.SIGUSR2: "SIGUSR2", 44 | C.SIGPIPE: "SIGPIPE", 45 | C.SIGALRM: "SIGALRM", 46 | C.SIGTERM: "SIGTERM", 47 | C.SIGSTKFLT: "SIGSTKFLT", 48 | C.SIGCHLD: "SIGCHLD", 49 | C.SIGCONT: "SIGCONT", 50 | C.SIGSTOP: "SIGSTOP", 51 | C.SIGTSTP: "SIGTSTP", 52 | C.SIGTTIN: "SIGTTIN", 53 | C.SIGTTOU: "SIGTTOU", 54 | C.SIGURG: "SIGURG", 55 | C.SIGXCPU: "SIGXCPU", 56 | C.SIGXFSZ: "SIGXFSZ", 57 | C.SIGVTALRM: "SIGVTALRM", 58 | C.SIGPROF: "SIGPROF", 59 | C.SIGWINCH: "SIGWINCH", 60 | C.SIGIO: "SIGIO", 61 | C.SIGPWR: "SIGPWR", 62 | C.SIGSYS: "SIGSYS", 63 | } 64 | 65 | func AnnotateSignal(arg Arg, _ int) { 66 | arg.SetAnnotation(SignalToString(int(arg.Raw())), true) 67 | } 68 | 69 | func SignalToString(signal int) string { 70 | var extra string 71 | if signal&0x80 != 0 { 72 | extra = "|0x80" 73 | signal &= ^0x80 74 | } 75 | if name, ok := signals[signal]; ok { 76 | return name + extra 77 | } 78 | return fmt.Sprintf("0x%x%s", signal, extra) 79 | } 80 | 81 | var signalCodes = map[syscall.Signal]map[int]string{ 82 | 0: { 83 | C.SI_USER: "SI_USER", 84 | C.SI_KERNEL: "SI_KERNEL", 85 | C.SI_QUEUE: "SI_QUEUE", 86 | C.SI_TIMER: "SI_TIMER", 87 | C.SI_MESGQ: "SI_MESGQ", 88 | C.SI_ASYNCIO: "SI_ASYNCIO", 89 | C.SI_SIGIO: "SI_SIGIO", 90 | C.SI_TKILL: "SI_TKILL", 91 | }, 92 | syscall.SIGILL: { 93 | C.ILL_ILLOPC: "ILL_ILLOPC", 94 | C.ILL_ILLOPN: "ILL_ILLOPN", 95 | C.ILL_ILLADR: "ILL_ILLADR", 96 | C.ILL_ILLTRP: "ILL_ILLTRP", 97 | C.ILL_PRVOPC: "ILL_PRVOPC", 98 | C.ILL_PRVREG: "ILL_PRVREG", 99 | C.ILL_COPROC: "ILL_COPROC", 100 | C.ILL_BADSTK: "ILL_BADSTK", 101 | }, 102 | syscall.SIGFPE: { 103 | C.FPE_INTDIV: "FPE_INTDIV", 104 | C.FPE_INTOVF: "FPE_INTOVF", 105 | C.FPE_FLTDIV: "FPE_FLTDIV", 106 | C.FPE_FLTOVF: "FPE_FLTOVF", 107 | C.FPE_FLTUND: "FPE_FLTUND", 108 | C.FPE_FLTRES: "FPE_FLTRES", 109 | C.FPE_FLTINV: "FPE_FLTINV", 110 | C.FPE_FLTSUB: "FPE_FLTSUB", 111 | }, 112 | syscall.SIGSEGV: { 113 | C.SEGV_MAPERR: "SEGV_MAPERR", 114 | C.SEGV_ACCERR: "SEGV_ACCERR", 115 | C.SEGV_BNDERR: "SEGV_BNDERR", 116 | C.SEGV_PKUERR: "SEGV_PKUERR", 117 | }, 118 | syscall.SIGBUS: { 119 | C.BUS_ADRALN: "BUS_ADRALN", 120 | C.BUS_ADRERR: "BUS_ADRERR", 121 | C.BUS_OBJERR: "BUS_OBJERR", 122 | C.BUS_MCEERR_AR: "BUS_MCEERR_AR", 123 | C.BUS_MCEERR_AO: "BUS_MCEERR_AO", 124 | }, 125 | syscall.SIGTRAP: { 126 | C.TRAP_BRKPT: "TRAP_BRKPT", 127 | C.TRAP_TRACE: "TRAP_TRACE", 128 | C.TRAP_BRANCH: "TRAP_BRANCH", 129 | C.TRAP_HWBKPT: "TRAP_HWBKPT", 130 | }, 131 | syscall.SIGCHLD: { 132 | C.CLD_EXITED: "CLD_EXITED", 133 | C.CLD_KILLED: "CLD_KILLED", 134 | C.CLD_DUMPED: "CLD_DUMPED", 135 | C.CLD_TRAPPED: "CLD_TRAPPED", 136 | C.CLD_STOPPED: "CLD_STOPPED", 137 | C.CLD_CONTINUED: "CLD_CONTINUED", 138 | }, 139 | syscall.SIGPOLL: { 140 | C.POLL_IN: "POLL_IN", 141 | C.POLL_OUT: "POLL_OUT", 142 | C.POLL_MSG: "POLL_MSG", 143 | C.POLL_ERR: "POLL_ERR", 144 | C.POLL_PRI: "POLL_PRI", 145 | C.POLL_HUP: "POLL_HUP", 146 | }, 147 | syscall.SIGSYS: { 148 | C.SYS_SECCOMP: "SYS_SECCOMP", 149 | }, 150 | } 151 | 152 | func SignalCodeToString(signal syscall.Signal, code int32) string { 153 | if codes, ok := signalCodes[signal]; ok { 154 | if str, ok := codes[int(code)]; ok { 155 | return str 156 | } 157 | } 158 | if str, ok := signalCodes[0][int(code)]; ok { 159 | return str 160 | } 161 | return fmt.Sprintf("%d", code) 162 | } 163 | 164 | var signalFDFlags = map[int]string{ 165 | unix.SFD_NONBLOCK: "SFD_NONBLOCK", 166 | unix.SFD_CLOEXEC: "SFD_CLOEXEC", 167 | } 168 | 169 | func AnnotateSignalFdFlags(arg Arg, _ int) { 170 | if str, ok := signalFDFlags[int(arg.Raw())]; ok { 171 | arg.SetAnnotation(str, true) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /tracer/annotation/sigstack.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | */ 6 | import "C" 7 | 8 | var sigStackFlags = map[int]string{ 9 | C.SS_AUTODISARM: "SS_AUTODISARM", 10 | } 11 | 12 | func AnnotateSigStackFlags(arg Arg, _ int) { 13 | for flag, name := range sigStackFlags { 14 | if int(arg.Raw())&flag != 0 { 15 | arg.SetAnnotation(name, true) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tracer/annotation/splice.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var spliceFlags = map[int]string{ 10 | unix.SPLICE_F_MOVE: "SPLICE_F_MOVE", 11 | unix.SPLICE_F_NONBLOCK: "SPLICE_F_NONBLOCK", 12 | unix.SPLICE_F_MORE: "SPLICE_F_MORE", 13 | unix.SPLICE_F_GIFT: "SPLICE_F_GIFT", 14 | } 15 | 16 | func AnnotateSpliceFlags(arg Arg, _ int) { 17 | var joins []string 18 | for flag, name := range spliceFlags { 19 | if (int(arg.Raw())&flag != 0) || (int(arg.Raw()) == flag) { 20 | joins = append(joins, name) 21 | } 22 | } 23 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 24 | } 25 | -------------------------------------------------------------------------------- /tracer/annotation/statx.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var statxFlags = map[int]string{ 10 | unix.STATX_TYPE: "STATX_TYPE", 11 | unix.STATX_MODE: "STATX_MODE", 12 | unix.STATX_NLINK: "STATX_NLINK", 13 | unix.STATX_UID: "STATX_UID", 14 | unix.STATX_GID: "STATX_GID", 15 | unix.STATX_ATIME: "STATX_ATIME", 16 | unix.STATX_MTIME: "STATX_MTIME", 17 | unix.STATX_CTIME: "STATX_CTIME", 18 | unix.STATX_INO: "STATX_INO", 19 | unix.STATX_SIZE: "STATX_SIZE", 20 | unix.STATX_BLOCKS: "STATX_BLOCKS", 21 | unix.STATX_BASIC_STATS: "STATX_BASIC_STATS", 22 | unix.STATX_BTIME: "STATX_BTIME", 23 | unix.STATX_MNT_ID: "STATX_MNT_ID", 24 | unix.STATX_ALL: "STATX_ALL", 25 | } 26 | 27 | func AnnotateStatxMask(arg Arg, _ int) { 28 | var joins []string 29 | for flag, str := range statxFlags { 30 | if int(arg.Raw())&flag != 0 { 31 | joins = append(joins, str) 32 | } 33 | } 34 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 35 | } 36 | -------------------------------------------------------------------------------- /tracer/annotation/syncfilerange.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var syncFileRangeFlags = map[int]string{ 10 | unix.SYNC_FILE_RANGE_WAIT_BEFORE: "SYNC_FILE_RANGE_WAIT_BEFORE", 11 | unix.SYNC_FILE_RANGE_WRITE: "SYNC_FILE_RANGE_WRITE", 12 | unix.SYNC_FILE_RANGE_WAIT_AFTER: "SYNC_FILE_RANGE_WAIT_AFTER", 13 | } 14 | 15 | func AnnotateSyncFileRangeFlags(arg Arg, _ int) { 16 | var joins []string 17 | for flag, name := range syncFileRangeFlags { 18 | if (int(arg.Raw())&flag != 0) || (int(arg.Raw()) == flag) { 19 | joins = append(joins, name) 20 | } 21 | } 22 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 23 | } 24 | -------------------------------------------------------------------------------- /tracer/annotation/syslog.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var syslogTypes = map[int]string{ 6 | unix.SYSLOG_ACTION_READ: "SYSLOG_ACTION_READ", 7 | unix.SYSLOG_ACTION_READ_ALL: "SYSLOG_ACTION_READ_ALL", 8 | unix.SYSLOG_ACTION_READ_CLEAR: "SYSLOG_ACTION_READ_CLEAR", 9 | unix.SYSLOG_ACTION_CLEAR: "SYSLOG_ACTION_CLEAR", 10 | unix.SYSLOG_ACTION_CONSOLE_OFF: "SYSLOG_ACTION_CONSOLE_OFF", 11 | unix.SYSLOG_ACTION_CONSOLE_ON: "SYSLOG_ACTION_CONSOLE_ON", 12 | unix.SYSLOG_ACTION_CONSOLE_LEVEL: "SYSLOG_ACTION_CONSOLE_LEVEL", 13 | unix.SYSLOG_ACTION_SIZE_UNREAD: "SYSLOG_ACTION_SIZE_UNREAD", 14 | unix.SYSLOG_ACTION_SIZE_BUFFER: "SYSLOG_ACTION_SIZE_BUFFER", 15 | } 16 | 17 | func AnnotateSyslogType(arg Arg, _ int) { 18 | if name, ok := syslogTypes[int(arg.Raw())]; ok { 19 | arg.SetAnnotation(name, true) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tracer/annotation/time.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | var clockStates = map[int]string{ 6 | unix.TIME_OK: "TIME_OK", 7 | unix.TIME_INS: "TIME_INS", 8 | unix.TIME_DEL: "TIME_DEL", 9 | unix.TIME_OOP: "TIME_OOP", 10 | unix.TIME_WAIT: "TIME_WAIT", 11 | unix.TIME_ERROR: "TIME_ERROR", 12 | } 13 | 14 | func AnnotateClockState(arg Arg, _ int) { 15 | if s, ok := clockStates[int(arg.Raw())]; ok { 16 | arg.SetAnnotation(s, true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tracer/annotation/timer.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func AnnotateWhichTimer(arg Arg, _ int) { 10 | switch arg.Raw() { 11 | case unix.ITIMER_REAL: 12 | arg.SetAnnotation("ITIMER_REAL", true) 13 | case unix.ITIMER_VIRTUAL: 14 | arg.SetAnnotation("ITIMER_VIRTUAL", true) 15 | case unix.ITIMER_PROF: 16 | arg.SetAnnotation("ITIMER_PROF", true) 17 | } 18 | } 19 | 20 | var timerFlags = map[int]string{ 21 | unix.TIMER_ABSTIME: "TIMER_ABSTIME", 22 | } 23 | 24 | func AnnotateTimerFlags(arg Arg, _ int) { 25 | if str, ok := timerFlags[int(arg.Raw())]; ok { 26 | arg.SetAnnotation(str, true) 27 | } 28 | } 29 | 30 | var timerFdFlags = map[int]string{ 31 | unix.O_CLOEXEC: "TFD_CLOEXEC", 32 | unix.O_NONBLOCK: "TFD_NONBLOCK", 33 | unix.TFD_TIMER_ABSTIME: "TFD_TIMER_ABSTIME", 34 | unix.TFD_TIMER_CANCEL_ON_SET: "TFD_TIMER_CANCEL_ON_SET", 35 | } 36 | 37 | func AnnotateTimerFdFlags(arg Arg, _ int) { 38 | var joins []string 39 | for flag, str := range timerFdFlags { 40 | if int(arg.Raw())&flag != 0 { 41 | joins = append(joins, str) 42 | } 43 | } 44 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 45 | } 46 | -------------------------------------------------------------------------------- /tracer/annotation/wait.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | 8 | int fWIFEXITED(int status) { 9 | return WIFEXITED(status); 10 | } 11 | int fWEXITSTATUS(int status) { 12 | return WEXITSTATUS(status); 13 | } 14 | int fWIFSIGNALED(int status) { 15 | return WIFSIGNALED(status); 16 | } 17 | int fWTERMSIG(int status) { 18 | return WTERMSIG(status); 19 | } 20 | int fWIFSTOPPED(int status) { 21 | return WIFSTOPPED(status); 22 | } 23 | int fWSTOPSIG(int status) { 24 | return WSTOPSIG(status); 25 | } 26 | */ 27 | import "C" 28 | 29 | import ( 30 | "fmt" 31 | "strings" 32 | "syscall" 33 | 34 | "golang.org/x/sys/unix" 35 | ) 36 | 37 | var waitOptions = map[int]string{ 38 | unix.WNOHANG: "WNOHANG", 39 | unix.WUNTRACED: "WUNTRACED", 40 | unix.WCONTINUED: "WCONTINUED", 41 | unix.WEXITED: "WEXITED", 42 | unix.WNOWAIT: "WNOWAIT", 43 | unix.WALL: "__WALL", 44 | unix.WNOTHREAD: "__WNOTHREAD", 45 | unix.WCLONE: "__WCLONE", 46 | } 47 | 48 | func AnnotateWaitOptions(arg Arg, _ int) { 49 | var joins []string 50 | for flag, name := range waitOptions { 51 | if arg.Raw()&uintptr(flag) != 0 { 52 | joins = append(joins, name) 53 | } 54 | } 55 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 56 | } 57 | 58 | func AnnotateWaitStatus(arg Arg, _ int) { 59 | status := syscall.WaitStatus(arg.Raw()) 60 | 61 | var joins []string 62 | 63 | if C.fWIFEXITED(C.int(status)) != 0 { 64 | joins = append(joins, "WIFEXITED(s)") 65 | result := C.fWEXITSTATUS(C.int(status)) 66 | joins = append(joins, fmt.Sprintf("WEXITSTATUS(s) == %d", result)) 67 | } 68 | 69 | if C.fWIFSIGNALED(C.int(status)) != 0 { 70 | joins = append(joins, "WIFSIGNALED(s)") 71 | result := int(C.fWTERMSIG(C.int(status))) 72 | joins = append(joins, fmt.Sprintf("WTERMSIG(s) == %s", SignalToString(result))) 73 | } 74 | 75 | if C.fWIFSTOPPED(C.int(status)) != 0 { 76 | joins = append(joins, "WIFSTOPPED(s)") 77 | result := int(C.fWSTOPSIG(C.int(status))) 78 | joins = append(joins, fmt.Sprintf("WSTOPSIG(s) == %s", SignalToString(result))) 79 | } 80 | 81 | arg.SetAnnotation(strings.Join(joins, " && "), len(joins) > 0) 82 | } 83 | 84 | var waitIDs = map[int]string{ 85 | unix.P_PID: "P_PID", 86 | unix.P_PGID: "P_PGID", 87 | unix.P_ALL: "P_ALL", 88 | } 89 | 90 | func AnnotateIDType(arg Arg, _ int) { 91 | if str, ok := waitIDs[int(arg.Raw())]; ok { 92 | arg.SetAnnotation(str, true) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /tracer/annotation/whence.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import "golang.org/x/sys/unix" 4 | 5 | func AnnotateWhence(arg Arg, _ int) { 6 | switch int(arg.Raw()) { 7 | case unix.SEEK_SET: 8 | arg.SetAnnotation("SEEK_SET", true) 9 | case unix.SEEK_CUR: 10 | arg.SetAnnotation("SEEK_CUR", true) 11 | case unix.SEEK_END: 12 | arg.SetAnnotation("SEEK_END", true) 13 | case unix.SEEK_DATA: 14 | arg.SetAnnotation("SEEK_DATA", true) 15 | case unix.SEEK_HOLE: 16 | arg.SetAnnotation("SEEK_HOLE", true) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tracer/annotation/x.go: -------------------------------------------------------------------------------- 1 | package annotation 2 | 3 | import ( 4 | "strings" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | var xFlags = map[int]string{ 10 | unix.XATTR_CREATE: "XATTR_CREATE", 11 | unix.XATTR_REPLACE: "XATTR_REPLACE", 12 | } 13 | 14 | func AnnotateXFlags(arg Arg, _ int) { 15 | var joins []string 16 | for flag, name := range xFlags { 17 | if int(arg.Raw())&flag != 0 { 18 | joins = append(joins, name) 19 | } 20 | } 21 | arg.SetAnnotation(strings.Join(joins, "|"), len(joins) > 0) 22 | } 23 | -------------------------------------------------------------------------------- /tracer/args.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "github.com/liamg/grace/tracer/annotation" 7 | ) 8 | 9 | type ArgMetadata struct { 10 | Name string 11 | Type ArgType 12 | Annotator func(arg annotation.Arg, pid int) 13 | LenSource LenSource 14 | Optional bool 15 | Destination bool 16 | FixedCount int 17 | } 18 | 19 | type LenSource uint8 20 | 21 | const ( 22 | LenSourceNone LenSource = iota 23 | LenSourcePrev 24 | LenSourceNext 25 | LenSourceNextPointer 26 | LenSourceReturnValue 27 | LenSourceFixed 28 | ) 29 | 30 | type ReturnMetadata ArgMetadata 31 | 32 | type Arg struct { 33 | name string 34 | t ArgType 35 | raw uintptr 36 | data []byte 37 | annotation string 38 | replace bool // replace value output with annotation 39 | bitSize int 40 | obj *Object 41 | array []Arg 42 | known bool 43 | } 44 | 45 | type Object struct { 46 | Name string 47 | Properties []Arg 48 | } 49 | 50 | func (s Arg) Known() bool { 51 | return s.known 52 | } 53 | 54 | func (s Arg) Name() string { 55 | return s.name 56 | } 57 | 58 | func (s Arg) Type() ArgType { 59 | return s.t 60 | } 61 | 62 | func (s Arg) Raw() uintptr { 63 | return s.raw 64 | } 65 | 66 | func (s Arg) Int() int { 67 | switch s.t { 68 | case ArgTypeLong: 69 | switch s.bitSize { 70 | case 32: 71 | return int(int32(s.raw)) 72 | case 64: 73 | return int(int64(s.raw)) 74 | } 75 | case ArgTypeInt, ArgTypeUnknown: 76 | // an int is 32-bit on both 32-bit and 64-bit linux 77 | return int(int32(s.raw)) 78 | } 79 | return int(s.raw) 80 | } 81 | 82 | func (s Arg) Data() []byte { 83 | return s.data 84 | } 85 | 86 | func (s Arg) Annotation() string { 87 | return s.annotation 88 | } 89 | 90 | func (s Arg) ReplaceValueWithAnnotation() bool { 91 | return s.replace 92 | } 93 | 94 | func (s Arg) Object() *Object { 95 | return s.obj 96 | } 97 | 98 | func (s Arg) Array() []Arg { 99 | return s.array 100 | } 101 | 102 | func (s *Arg) SetAnnotation(annotation string, replace bool) { 103 | s.annotation = annotation 104 | s.replace = replace 105 | } 106 | 107 | func processArgument(raw uintptr, next, prev uintptr, ret uintptr, metadata ArgMetadata, pid int, exit bool) (*Arg, error) { 108 | arg := &Arg{ 109 | name: metadata.Name, 110 | t: metadata.Type, 111 | raw: raw, 112 | bitSize: bitSize, 113 | known: true, 114 | } 115 | 116 | // if we're on the syscall enter and the argument is a pointer for a destination, we don't know the value yet 117 | if !exit && metadata.Destination { 118 | arg.known = false 119 | return arg, nil 120 | } 121 | 122 | // resolve next to int from next pointer 123 | if metadata.LenSource == LenSourceNextPointer && (exit || !metadata.Destination) && next > 0 { 124 | var realNext uint32 125 | buf, err := readSize(pid, next, unsafe.Sizeof(realNext)) 126 | if err != nil { 127 | return nil, err 128 | } 129 | next = uintptr(decodeInt(buf)) 130 | } 131 | 132 | // process the argument data into something meaningful 133 | if err := handleType(arg, metadata, raw, next, prev, ret, pid); err != nil { 134 | return nil, err 135 | } 136 | 137 | // always apply annotations 138 | if metadata.Annotator != nil { 139 | metadata.Annotator(arg, pid) 140 | } 141 | 142 | return arg, nil 143 | } 144 | -------------------------------------------------------------------------------- /tracer/decode.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "reflect" 7 | ) 8 | 9 | func decodeStruct(memory []byte, target interface{}) error { 10 | 11 | sType := reflect.TypeOf(target) 12 | if sType.Kind() != reflect.Ptr { 13 | return errors.New("target must be a pointer") 14 | } 15 | 16 | var index uintptr 17 | 18 | sPtrValue := reflect.ValueOf(target) 19 | sValue := sPtrValue.Elem() 20 | 21 | switch sValue.Kind() { 22 | case reflect.Struct: 23 | for i := 0; i < sValue.Type().NumField(); i++ { 24 | size := sValue.Type().Field(i).Type.Size() 25 | if sValue.Type().Field(i).Name == "_" { 26 | index += size 27 | continue 28 | } 29 | raw := memory[index : index+size] 30 | if err := decodeAnonymous(sValue.Field(i), raw); err != nil { 31 | return err 32 | } 33 | index += size 34 | } 35 | default: 36 | return errors.New("target must be a pointer to a struct") 37 | } 38 | 39 | return nil 40 | } 41 | 42 | func decodeAnonymous(target reflect.Value, raw []byte) error { 43 | 44 | if target.Kind() == reflect.Ptr { 45 | target.Set(reflect.New(target.Type().Elem())) 46 | target = target.Elem() 47 | } 48 | 49 | if !target.CanSet() { 50 | return fmt.Errorf("cannot set %s", target.Type()) 51 | } 52 | 53 | switch target.Kind() { 54 | case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint, reflect.Uintptr: 55 | target.SetUint(decodeUint(raw)) 56 | case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int: 57 | target.SetInt(decodeInt(raw)) 58 | case reflect.String: 59 | target.SetString(string(raw)) 60 | case reflect.Struct: 61 | if err := decodeStruct(raw, target.Addr().Interface()); err != nil { 62 | return err 63 | } 64 | case reflect.Array, reflect.Slice: 65 | var index uintptr 66 | for i := 0; i < target.Len(); i++ { 67 | memory := raw[index : index+target.Type().Elem().Size()] 68 | if err := decodeAnonymous(target.Index(i), memory); err != nil { 69 | return err 70 | } 71 | index += target.Type().Elem().Size() 72 | } 73 | default: 74 | return fmt.Errorf("unsupported kind for field '%s': %s", target.String(), target.Kind().String()) 75 | } 76 | return nil 77 | } 78 | 79 | func decodeUint(raw []byte) uint64 { 80 | var output uint64 81 | for i := 0; i < len(raw); i++ { 82 | output |= uint64(raw[i]) << uint(i*8) 83 | } 84 | return output 85 | } 86 | func decodeInt(raw []byte) int64 { 87 | var output int64 88 | for i := 0; i < len(raw); i++ { 89 | output |= int64(raw[i]) << uint(i*8) 90 | } 91 | return output 92 | } 93 | -------------------------------------------------------------------------------- /tracer/decode_uint_test.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import "testing" 4 | 5 | func Test_DecodeUint(t *testing.T) { 6 | 7 | tests := []struct { 8 | name string 9 | data []byte 10 | want uint64 11 | }{ 12 | { 13 | name: "zero length", 14 | data: []byte{}, 15 | want: 0, 16 | }, 17 | { 18 | name: "filled byte", 19 | data: []byte{ 20 | 0xff, 21 | }, 22 | want: 0xff, 23 | }, 24 | { 25 | name: "byte order", 26 | data: []byte{ 27 | 0x11, 28 | 0xff, 29 | 0x00, 30 | }, 31 | want: 0xff11, 32 | }, 33 | { 34 | name: "byte max value", 35 | data: []byte{ 36 | 0xff, 37 | 0xff, 38 | 0xff, 39 | 0xff, 40 | 0xff, 41 | 0xff, 42 | 0xff, 43 | 0xff, 44 | }, 45 | want: 0xffffffffffffffff, 46 | }, 47 | { 48 | name: "overflow ignored", 49 | data: []byte{ 50 | 0xff, 51 | 0xff, 52 | 0xff, 53 | 0xff, 54 | 0xff, 55 | 0xff, 56 | 0xff, 57 | 0xff, 58 | 0x1, 59 | }, 60 | want: 0xffffffffffffffff, 61 | }, 62 | } 63 | 64 | for _, test := range tests { 65 | t.Run(test.name, func(t *testing.T) { 66 | if got := decodeUint(test.data); got != test.want { 67 | t.Errorf("decodeUint() = %v, want %v", got, test.want) 68 | } 69 | }) 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /tracer/netw/netw.go: -------------------------------------------------------------------------------- 1 | package netw 2 | 3 | func ListConnections() ([]Connection, error) { 4 | tcpConnections, err := ListTCPConnections() 5 | if err != nil { 6 | return nil, err 7 | } 8 | udpConnections, err := ListUDPConnections() 9 | if err != nil { 10 | return nil, err 11 | } 12 | icmpConnections, err := ListICMPConnections() 13 | if err != nil { 14 | return nil, err 15 | } 16 | return append(append(tcpConnections, udpConnections...), icmpConnections...), nil 17 | } 18 | -------------------------------------------------------------------------------- /tracer/netw/proc.go: -------------------------------------------------------------------------------- 1 | package netw 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "os" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type Connection struct { 12 | Protocol string 13 | LocalAddress net.IP 14 | LocalPort int 15 | RemoteAddress net.IP 16 | RemotePort int 17 | INode int 18 | State int // TCP_ESTABLISHED etc. see https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp_states.h 19 | } 20 | 21 | const netPath = "/proc/net/" 22 | 23 | func ListTCPConnections() ([]Connection, error) { 24 | return parseBaseProtocol("tcp") 25 | } 26 | 27 | func ListUDPConnections() ([]Connection, error) { 28 | return parseBaseProtocol("udp") 29 | } 30 | 31 | func ListICMPConnections() ([]Connection, error) { 32 | return parseBaseProtocol("icmp") 33 | } 34 | 35 | func parseBaseProtocol(protocol string) ([]Connection, error) { 36 | v4, err := parseFile(protocol) 37 | if err != nil { 38 | return nil, err 39 | } 40 | 41 | v6, err := parseFile(protocol + "6") 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | return append(v4, v6...), nil 47 | } 48 | 49 | func parseFile(protocol string) ([]Connection, error) { 50 | 51 | file := netPath + protocol 52 | data, err := os.ReadFile(file) 53 | if err != nil { 54 | return nil, err 55 | } 56 | var connections []Connection 57 | lines := strings.Split(string(data), "\n") 58 | for _, line := range lines { 59 | line = strings.TrimSpace(line) 60 | if line == "" { 61 | continue 62 | } 63 | if strings.HasPrefix(line, "sl") { // skip headings 64 | continue 65 | } 66 | connection, err := parseLine(line) 67 | if err != nil { 68 | return nil, err 69 | } 70 | connection.Protocol = protocol 71 | connections = append(connections, *connection) 72 | } 73 | return connections, nil 74 | } 75 | 76 | func parseLine(line string) (*Connection, error) { 77 | 78 | line = strings.TrimSpace(line) 79 | fields := strings.Fields(line) 80 | 81 | if len(fields) < 10 { 82 | return nil, fmt.Errorf("invalid tcp connection: %s", line) 83 | } 84 | 85 | inode, err := strconv.Atoi(fields[9]) 86 | if err != nil { 87 | return nil, fmt.Errorf("invalid inode '%s': %w", fields[9], err) 88 | } 89 | 90 | localip, localport, err := parseIPAndPortFromHex(fields[1]) 91 | if err != nil { 92 | return nil, fmt.Errorf("invalid local ip '%s': %w", fields[1], err) 93 | } 94 | 95 | remoteip, remoteport, err := parseIPAndPortFromHex(fields[2]) 96 | if err != nil { 97 | return nil, fmt.Errorf("invalid remote ip '%s': %w", fields[2], err) 98 | } 99 | 100 | return &Connection{ 101 | INode: inode, 102 | LocalAddress: localip, 103 | LocalPort: localport, 104 | RemoteAddress: remoteip, 105 | RemotePort: remoteport, 106 | State: int(hexToByte(fields[3])), 107 | }, nil 108 | } 109 | 110 | func parseIPAndPortFromHex(hex string) (net.IP, int, error) { 111 | 112 | rawip, rawport, found := strings.Cut(hex, ":") 113 | if !found { 114 | return nil, 0, fmt.Errorf("invalid hex '%s'", hex) 115 | } 116 | 117 | var ip net.IP 118 | 119 | switch len(rawip) { 120 | case 8: 121 | ip = net.IPv4( 122 | hexToByte(rawip[6:8]), 123 | hexToByte(rawip[4:6]), 124 | hexToByte(rawip[2:4]), 125 | hexToByte(rawip[0:2]), 126 | ) 127 | case 32: 128 | ip = []byte{ 129 | hexToByte(rawip[30:32]), 130 | hexToByte(rawip[28:30]), 131 | hexToByte(rawip[26:28]), 132 | hexToByte(rawip[24:26]), 133 | hexToByte(rawip[22:24]), 134 | hexToByte(rawip[20:22]), 135 | hexToByte(rawip[18:20]), 136 | hexToByte(rawip[16:18]), 137 | hexToByte(rawip[14:16]), 138 | hexToByte(rawip[12:14]), 139 | hexToByte(rawip[10:12]), 140 | hexToByte(rawip[8:10]), 141 | hexToByte(rawip[6:8]), 142 | hexToByte(rawip[4:6]), 143 | hexToByte(rawip[2:4]), 144 | hexToByte(rawip[0:2]), 145 | } 146 | default: 147 | return nil, 0, fmt.Errorf("invalid ipv4 hex '%s'", hex) 148 | } 149 | 150 | port, err := strconv.ParseInt(rawport, 16, 32) 151 | if err != nil { 152 | return nil, 0, fmt.Errorf("invalid port '%s': %w", rawport, err) 153 | } 154 | 155 | return ip, int(port), nil 156 | } 157 | 158 | func hexToByte(hex string) byte { 159 | b, err := strconv.ParseUint(hex, 16, 8) 160 | if err != nil { 161 | return 0 162 | } 163 | return byte(b) 164 | } 165 | -------------------------------------------------------------------------------- /tracer/signal.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | type SigInfo struct { 12 | Signo int32 13 | Errno int32 14 | Code int32 15 | TrapNo int32 16 | Pid int32 17 | Uid int32 18 | } 19 | 20 | func getSignalInfo(pid int) (*SigInfo, error) { 21 | var info SigInfo 22 | _, _, e1 := syscall.Syscall6(syscall.SYS_PTRACE, uintptr(unix.PTRACE_GETSIGINFO), uintptr(pid), 0, uintptr(unsafe.Pointer(&info)), 0, 0) 23 | if e1 != 0 { 24 | return nil, fmt.Errorf("ptrace get signal info failed: %v", e1) 25 | } 26 | return &info, nil 27 | } 28 | -------------------------------------------------------------------------------- /tracer/sys.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | type Syscall struct { 10 | pid int 11 | number int 12 | rawArgs [6]uintptr 13 | args []Arg 14 | rawRet uintptr 15 | ret Arg 16 | unknown bool 17 | paths []string 18 | complete bool 19 | } 20 | 21 | type SyscallMetadata struct { 22 | Name string 23 | Args []ArgMetadata 24 | ReturnValue ReturnMetadata 25 | Modifier func(call *Syscall) 26 | } 27 | 28 | func (s *Syscall) Number() int { 29 | return s.number 30 | } 31 | 32 | func (s *Syscall) Paths() []string { 33 | return s.paths 34 | } 35 | 36 | func (s *Syscall) Name() string { 37 | meta, ok := sysMap[s.number] 38 | if !ok { 39 | return fmt.Sprintf("unknown_syscall_%d", s.number) 40 | } 41 | return meta.Name 42 | } 43 | 44 | func (s *Syscall) Args() []Arg { 45 | return s.args 46 | } 47 | 48 | func (s *Syscall) Return() Arg { 49 | return s.ret 50 | } 51 | 52 | func (s *Syscall) Unknown() bool { 53 | return s.unknown 54 | } 55 | 56 | func (s *Syscall) Complete() bool { 57 | return s.complete 58 | } 59 | 60 | func (s *Syscall) populate(exit bool) error { 61 | meta, ok := sysMap[s.number] 62 | if !ok { 63 | s.unknown = true 64 | } 65 | 66 | if exit { 67 | ret, err := processArgument(s.rawRet, 0, 0, 0, ArgMetadata(meta.ReturnValue), s.pid, exit) 68 | if err != nil { 69 | return fmt.Errorf("failed to set return value of syscall %s (%d): %w", meta.Name, s.number, err) 70 | } 71 | s.ret = *ret 72 | } 73 | for i, argMeta := range meta.Args { 74 | if exit && !argMeta.Destination && i < len(s.args) { 75 | continue 76 | } 77 | 78 | var next uintptr 79 | if i < len(meta.Args)-1 { 80 | next = s.rawArgs[i+1] 81 | } 82 | var prev uintptr 83 | if i > 0 { 84 | prev = s.rawArgs[i-1] 85 | } 86 | 87 | arg, err := processArgument(s.rawArgs[i], next, prev, s.rawRet, argMeta, s.pid, exit) 88 | if err != nil { 89 | return fmt.Errorf("failed to set argument %d (%s) of syscall %s (%d): %w", i, argMeta.Name, meta.Name, s.number, err) 90 | } 91 | if !arg.known { 92 | break 93 | } 94 | if i >= len(s.args) { 95 | s.args = append(s.args, *arg) 96 | } else { 97 | s.args[i] = *arg 98 | } 99 | 100 | // best attempt to set path information 101 | if argMeta.Type == argTypeString && (strings.Contains(argMeta.Name, "path") || strings.Contains(argMeta.Name, "file")) { 102 | s.paths = append(s.paths, string(arg.Data())) 103 | } else if argMeta.Type == ArgTypeInt && strings.Contains(argMeta.Name, "fd") { 104 | if path, err := os.Readlink(fmt.Sprintf("/proc/%d/fd/%d", s.pid, arg.Raw())); err == nil { 105 | s.paths = append(s.paths, path) 106 | } 107 | } 108 | } 109 | s.complete = len(s.args) == len(meta.Args) 110 | for _, arg := range s.args { 111 | if !arg.known { 112 | s.complete = false 113 | break 114 | } 115 | } 116 | 117 | // strip off trailing optional args if they have no value 118 | var lastIndex int 119 | for i, arg := range s.args { 120 | meta := meta.Args[i] 121 | if !meta.Optional || arg.Raw() > 0 { 122 | lastIndex = i 123 | } 124 | } 125 | if lastIndex < len(s.args)-1 { 126 | s.args = s.args[:lastIndex+1] 127 | } 128 | 129 | if exit && meta.Modifier != nil { 130 | meta.Modifier(s) 131 | } 132 | 133 | return nil 134 | } 135 | -------------------------------------------------------------------------------- /tracer/sys_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build arm64 2 | 3 | package tracer 4 | 5 | import ( 6 | "syscall" 7 | ) 8 | 9 | const bitSize = 64 10 | 11 | // useful info: https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md 12 | 13 | func parseSyscall(regs *syscall.PtraceRegs) *Syscall { 14 | return &Syscall{ 15 | number: int(regs.Regs[8]), 16 | rawArgs: [6]uintptr{ 17 | uintptr(regs.Regs[0]), 18 | uintptr(regs.Regs[1]), 19 | uintptr(regs.Regs[2]), 20 | uintptr(regs.Regs[3]), 21 | uintptr(regs.Regs[4]), 22 | uintptr(regs.Regs[5]), 23 | }, 24 | rawRet: uintptr(regs.Regs[0]), 25 | } 26 | } 27 | 28 | // TODO: add syscall table for arm64 29 | var sysMap = map[int]SyscallMetadata{} 30 | -------------------------------------------------------------------------------- /tracer/sys_test.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func Test_SyscallSupport(t *testing.T) { 12 | 13 | for number, meta := range sysMap { 14 | t.Run(fmt.Sprintf("syscall %d: %s", number, meta.Name), func(t *testing.T) { 15 | checkSyscall(t, number, meta) 16 | }) 17 | } 18 | } 19 | 20 | func checkSyscall(t *testing.T, number int, meta SyscallMetadata) { 21 | require.NotNil(t, meta) 22 | assert.NotEmpty(t, meta.Name) 23 | assert.NotEqualf(t, ArgTypeUnknown, meta.ReturnValue.Type, "syscall %d (%s) has unspecified return value type", number, meta.Name) 24 | for i, arg := range meta.Args { 25 | assert.NotEqualf(t, ArgTypeUnknown, arg.Type, "syscall %d (%s) has unspecified argument type", number, meta.Name) 26 | switch arg.Type { 27 | case ArgTypeData: 28 | assert.NotEqual(t, LenSourceNone, arg.LenSource) 29 | } 30 | switch arg.LenSource { 31 | case LenSourceFixed: 32 | assert.NotZero(t, arg.FixedCount) 33 | case LenSourceNextPointer, LenSourceNext: 34 | assert.Less(t, i, len(meta.Args)-1) 35 | case LenSourcePrev: 36 | assert.Greater(t, i, 0) 37 | case LenSourceReturnValue: 38 | assert.NotEqual(t, ArgTypeErrorCode, meta.ReturnValue.Type) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tracer/type.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | type ArgType int 9 | 10 | const ( 11 | // public 12 | ArgTypeUnknown ArgType = iota 13 | ArgTypeData 14 | ArgTypeInt 15 | ArgTypeLong 16 | ArgTypeAddress 17 | ArgTypeUnsignedInt 18 | ArgTypeUnsignedLong 19 | ArgTypeObject 20 | ArgTypeErrorCode 21 | ArgTypeArray 22 | 23 | argStartInternal // TODO: use this to test all type converters are registered 24 | 25 | // internal 26 | argTypeString 27 | argTypeSockaddr 28 | argTypeIntOrErrorCode 29 | argTypeStat 30 | argTypePollFdArray 31 | argTypeSigAction 32 | argTypeIovecArray 33 | argTypeIntArray 34 | argTypeStringArray 35 | argTypeFdSet 36 | argTypeTimeval 37 | argTypeTimevalArray 38 | argTypeTimezone 39 | argTypeSHMIDDS 40 | argTypeTimespec 41 | argTypeTimespecArray 42 | argTypeItimerval 43 | argTypeMsghdr 44 | argTypeUnsignedIntPtr 45 | argTypeUnsignedInt64Ptr 46 | argTypeSockoptval 47 | argTypeWaitStatus 48 | argTypeRUsage 49 | argTypeRLimit 50 | argTypeUname 51 | argTypeSembuf 52 | argTypeSysinfo 53 | argTypeTms 54 | argTypeCapUserHeader 55 | argTypeCapUserData 56 | argTypeSigInfo 57 | argTypeStack 58 | argTypeUtimbuf 59 | argTypeUstat 60 | argTypeStatfs 61 | argTypeSchedParam 62 | argTypeUserDesc 63 | argTypeTimex 64 | argTypeIoEvent 65 | argTypeIoEvents 66 | argTypeIoCB 67 | argTypeItimerspec 68 | argTypeEpollEvent 69 | argTypeMqAttr 70 | argTypeMMsgHdrArray 71 | argTypeSchedAttr 72 | argTypeStatX 73 | argTypeIoUringParams 74 | argTypeCloneArgs 75 | argTypeOpenHow 76 | argTypeMountAttr 77 | argTypeLandlockRulesetAttr 78 | 79 | argEndInternal 80 | ) 81 | 82 | type typeHandler func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error 83 | 84 | var typesRegistry = map[ArgType]typeHandler{} 85 | var typesRegistryMutex = sync.RWMutex{} 86 | 87 | func registerTypeHandler(t ArgType, h typeHandler) { 88 | typesRegistryMutex.Lock() 89 | defer typesRegistryMutex.Unlock() 90 | if _, ok := typesRegistry[t]; ok { 91 | panic(fmt.Sprintf("type handler for %d already registered", t)) 92 | } 93 | typesRegistry[t] = h 94 | } 95 | 96 | func getHandler(t ArgType) typeHandler { 97 | typesRegistryMutex.RLock() 98 | defer typesRegistryMutex.RUnlock() 99 | return typesRegistry[t] 100 | } 101 | 102 | func handleType(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) (err error) { 103 | typesRegistryMutex.RLock() 104 | defer typesRegistryMutex.RUnlock() 105 | defer func() { 106 | if r := recover(); r != nil { 107 | err = fmt.Errorf("error handling type %s with value 0x%x: %v", metadata.Name, raw, r) 108 | } 109 | }() 110 | if h, ok := typesRegistry[metadata.Type]; ok { 111 | return h(arg, metadata, raw, next, prev, ret, pid) 112 | } 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /tracer/type_test.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestTypeConversions(t *testing.T) { 9 | for i := argStartInternal + 1; i < argEndInternal; i++ { 10 | t.Run(fmt.Sprintf("ArgType %d", i), func(t *testing.T) { 11 | if getHandler(i) == nil { 12 | t.Errorf("No handler for type %d", i) 13 | } 14 | }) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tracer/types_cap.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type capHeader struct { 8 | Version uint32 9 | Pid int32 10 | } 11 | 12 | type capData struct { 13 | Effective uint32 14 | Permitted uint32 15 | Inheritable uint32 16 | } 17 | 18 | func init() { 19 | registerTypeHandler(argTypeCapUserHeader, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 20 | if raw > 0 { 21 | mem, err := readSize(pid, raw, unsafe.Sizeof(capHeader{})) 22 | if err != nil { 23 | return err 24 | } 25 | var cap capHeader 26 | if err := decodeStruct(mem, &cap); err != nil { 27 | return err 28 | } 29 | arg.obj = convertCapHeader(&cap) 30 | arg.t = ArgTypeObject 31 | } else { 32 | arg.t = ArgTypeAddress 33 | arg.annotation = "NULL" 34 | arg.replace = true 35 | } 36 | return nil 37 | }) 38 | registerTypeHandler(argTypeCapUserData, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 39 | if raw > 0 { 40 | mem, err := readSize(pid, raw, unsafe.Sizeof(capData{})) 41 | if err != nil { 42 | return err 43 | } 44 | var cap capData 45 | if err := decodeStruct(mem, &cap); err != nil { 46 | return err 47 | } 48 | arg.obj = convertCapData(&cap) 49 | arg.t = ArgTypeObject 50 | } else { 51 | arg.t = ArgTypeAddress 52 | arg.annotation = "NULL" 53 | arg.replace = true 54 | } 55 | return nil 56 | }) 57 | } 58 | 59 | func convertCapHeader(cap *capHeader) *Object { 60 | return &Object{ 61 | Name: "hdr", 62 | Properties: []Arg{ 63 | { 64 | name: "version", 65 | t: ArgTypeInt, 66 | raw: uintptr(cap.Version), 67 | }, 68 | { 69 | name: "pid", 70 | t: ArgTypeInt, 71 | raw: uintptr(cap.Pid), 72 | }, 73 | }, 74 | } 75 | } 76 | 77 | func convertCapData(cap *capData) *Object { 78 | return &Object{ 79 | Name: "data", 80 | Properties: []Arg{ 81 | { 82 | name: "effective", 83 | t: ArgTypeInt, 84 | raw: uintptr(cap.Effective), 85 | }, 86 | { 87 | name: "permitted", 88 | t: ArgTypeInt, 89 | raw: uintptr(cap.Permitted), 90 | }, 91 | { 92 | name: "inheritable", 93 | t: ArgTypeInt, 94 | raw: uintptr(cap.Inheritable), 95 | }, 96 | }, 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tracer/types_cloneargs.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | // used by clone3 syscall 8 | type cloneArgs struct { 9 | Flags uint64 10 | PidFd uint64 11 | ChildTid uint64 12 | ParentTid uint64 13 | ExitSignal uint64 14 | Stack uint64 15 | StackSize uint64 16 | SetTLS uint64 17 | SetTid uint64 18 | SetTidSize uint64 19 | Cgroup uint64 20 | } 21 | 22 | func init() { 23 | registerTypeHandler(argTypeCloneArgs, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 24 | 25 | if raw > 0 { 26 | // read the raw C struct from the process memory 27 | mem, err := readSize(pid, raw, unsafe.Sizeof(cloneArgs{})) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | var args cloneArgs 33 | if err := decodeStruct(mem, &args); err != nil { 34 | return err 35 | } 36 | 37 | arg.obj = convertCloneArgs(args) 38 | arg.t = ArgTypeObject 39 | } else { 40 | arg.annotation = "NULL" 41 | arg.replace = true 42 | } 43 | return nil 44 | }) 45 | } 46 | 47 | func convertCloneArgs(args cloneArgs) *Object { 48 | return &Object{ 49 | Name: "clone_args", 50 | Properties: []Arg{ 51 | { 52 | name: "flags", 53 | t: ArgTypeUnsignedLong, 54 | raw: uintptr(args.Flags), 55 | }, 56 | { 57 | name: "pid_fd", 58 | t: ArgTypeUnsignedLong, 59 | raw: uintptr(args.PidFd), 60 | }, 61 | { 62 | name: "child_tid", 63 | t: ArgTypeUnsignedLong, 64 | raw: uintptr(args.ChildTid), 65 | }, 66 | { 67 | name: "parent_tid", 68 | t: ArgTypeUnsignedLong, 69 | raw: uintptr(args.ParentTid), 70 | }, 71 | { 72 | name: "exit_signal", 73 | t: ArgTypeUnsignedLong, 74 | raw: uintptr(args.ExitSignal), 75 | }, 76 | { 77 | name: "stack", 78 | t: ArgTypeUnsignedLong, 79 | raw: uintptr(args.Stack), 80 | }, 81 | { 82 | name: "stack_size", 83 | t: ArgTypeUnsignedLong, 84 | raw: uintptr(args.StackSize), 85 | }, 86 | { 87 | name: "set_tls", 88 | t: ArgTypeUnsignedLong, 89 | raw: uintptr(args.SetTLS), 90 | }, 91 | { 92 | name: "set_tid", 93 | t: ArgTypeUnsignedLong, 94 | raw: uintptr(args.SetTid), 95 | }, 96 | { 97 | name: "set_tid_size", 98 | t: ArgTypeUnsignedLong, 99 | raw: uintptr(args.SetTidSize), 100 | }, 101 | { 102 | name: "cgroup", 103 | t: ArgTypeUnsignedLong, 104 | raw: uintptr(args.Cgroup), 105 | }, 106 | }, 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /tracer/types_data.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | ) 7 | 8 | func init() { 9 | registerTypeHandler(ArgTypeData, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 10 | switch metadata.LenSource { 11 | case LenSourceNextPointer: 12 | if next == 0 { 13 | return nil 14 | } 15 | if buf, err := readSize(pid, next, 4); err == nil { 16 | size := uintptr(decodeInt(buf)) 17 | data, err := readSize(pid, raw, size) 18 | if err != nil { 19 | return err 20 | } 21 | arg.data = data 22 | } 23 | case LenSourcePrev: 24 | data, err := readSize(pid, raw, prev) 25 | if err != nil { 26 | return err 27 | } 28 | arg.data = data 29 | case LenSourceNext: 30 | data, err := readSize(pid, raw, next) 31 | if err != nil { 32 | return err 33 | } 34 | arg.data = data 35 | case LenSourceReturnValue: 36 | data, err := readSize(pid, raw, ret) 37 | if err != nil { 38 | return err 39 | } 40 | arg.data = data 41 | default: 42 | return fmt.Errorf("syscall %s has no supported count location", metadata.Name) 43 | } 44 | return nil 45 | }) 46 | } 47 | 48 | func readSize(pid int, addr uintptr, size uintptr) ([]byte, error) { 49 | 50 | if size == 0 || size>>(bitSize-1) == 1 { // if negative for this arch 51 | return nil, nil 52 | } 53 | data := make([]byte, size) 54 | count, err := syscall.PtracePeekData(pid, addr, data) 55 | if err != nil { 56 | return nil, fmt.Errorf("read of 0x%x (%d) failed: %w", addr, size, err) 57 | } 58 | return data[:count], nil 59 | } 60 | -------------------------------------------------------------------------------- /tracer/types_epollevent.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeEpollEvent, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | // read the raw C struct from the process memory 14 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.EpollEvent{})) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | var event unix.EpollEvent 20 | if err := decodeStruct(rawTimeVal, &event); err != nil { 21 | return err 22 | } 23 | 24 | arg.obj = convertEpollEvent(&event) 25 | arg.t = ArgTypeObject 26 | } else { 27 | arg.annotation = "NULL" 28 | arg.replace = true 29 | } 30 | return nil 31 | }) 32 | } 33 | 34 | func convertEpollEvent(ev *unix.EpollEvent) *Object { 35 | if ev == nil { 36 | return nil 37 | } 38 | return &Object{ 39 | Name: "epoll_event", 40 | Properties: []Arg{ 41 | { 42 | name: "events", 43 | t: ArgTypeUnsignedLong, 44 | raw: uintptr(ev.Events), 45 | }, 46 | { 47 | name: "fd", 48 | t: ArgTypeAddress, 49 | raw: uintptr(ev.Fd), 50 | }, 51 | }, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tracer/types_error.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | func init() { 4 | registerTypeHandler(argTypeIntOrErrorCode, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 5 | if arg.Int() < 0 { 6 | arg.t = ArgTypeErrorCode 7 | } else { 8 | arg.t = ArgTypeInt 9 | } 10 | return nil 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /tracer/types_fdset.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "github.com/liamg/grace/tracer/annotation" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | func init() { 12 | registerTypeHandler(argTypeFdSet, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 13 | if raw > 0 { 14 | // read the raw C struct from the process memory 15 | rawFdset, err := readSize(pid, raw, unsafe.Sizeof(unix.FdSet{})) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | // safely squish it into a struct in our own memory space 21 | var fdset unix.FdSet 22 | if err := decodeStruct(rawFdset, &fdset); err != nil { 23 | return err 24 | } 25 | 26 | // convert into a nice array for output 27 | arg.array = convertFdset(&fdset, pid) 28 | } 29 | arg.t = ArgTypeArray 30 | return nil 31 | }) 32 | } 33 | 34 | func convertFdset(fdset *unix.FdSet, pid int) []Arg { 35 | var fds []Arg 36 | for _, fd := range fdset.Bits { 37 | if fd == 0 { 38 | break 39 | } 40 | item := Arg{ 41 | name: "fd", 42 | t: ArgTypeInt, 43 | raw: uintptr(fd), 44 | } 45 | annotation.AnnotateFd(&item, pid) 46 | fds = append(fds, item) 47 | } 48 | return fds 49 | } 50 | -------------------------------------------------------------------------------- /tracer/types_int.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | registerTypeHandler(argTypeIntArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 10 | var count int 11 | switch metadata.LenSource { 12 | case LenSourcePrev: 13 | count = int(prev) 14 | case LenSourceNext: 15 | count = int(next) 16 | case LenSourceReturnValue: 17 | count = int(ret) 18 | case LenSourceFixed: 19 | count = metadata.FixedCount 20 | default: 21 | return fmt.Errorf("syscall %s has no supported count location", metadata.Name) 22 | } 23 | 24 | mem, err := readSize(pid, raw, 4*uintptr(count)) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | target := make([]int32, count) 30 | if err := decodeAnonymous(reflect.ValueOf(&target).Elem(), mem); err != nil { 31 | return err 32 | } 33 | 34 | arg.array = nil 35 | for i := 0; i < count; i++ { 36 | arg.array = append(arg.array, Arg{ 37 | t: ArgTypeInt, 38 | raw: uintptr(target[i]), 39 | }) 40 | } 41 | arg.t = ArgTypeArray 42 | 43 | return nil 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /tracer/types_int_ptr.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | func init() { 8 | registerTypeHandler(argTypeUnsignedIntPtr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 9 | var underlying uint32 10 | if buf, err := readSize(pid, next, unsafe.Sizeof(underlying)); err == nil { 11 | arg.raw = uintptr(decodeInt(buf)) 12 | arg.t = ArgTypeUnsignedInt 13 | } else { 14 | arg.t = ArgTypeAddress 15 | if raw == 0 { 16 | arg.annotation = "NULL" 17 | arg.replace = true 18 | } 19 | } 20 | return nil 21 | }) 22 | registerTypeHandler(argTypeUnsignedInt64Ptr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 23 | var underlying uint64 24 | if buf, err := readSize(pid, next, unsafe.Sizeof(underlying)); err == nil { 25 | arg.raw = uintptr(decodeInt(buf)) 26 | arg.t = ArgTypeUnsignedInt 27 | } else { 28 | arg.t = ArgTypeAddress 29 | if raw == 0 { 30 | arg.annotation = "NULL" 31 | arg.replace = true 32 | } 33 | } 34 | return nil 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /tracer/types_iocb.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type iocb struct { 8 | Data uint64 9 | Key uint32 10 | Opcode uint16 11 | Priority uint16 12 | Flags uint32 13 | Fd uint32 14 | Offset uint64 15 | Addr uint64 16 | Len uint32 17 | Pos uint64 18 | Reserved2 uint32 19 | Reserved3 uint64 20 | } 21 | 22 | func init() { 23 | registerTypeHandler(argTypeIoCB, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 24 | 25 | if raw > 0 { 26 | 27 | // read the raw C struct from the process memory 28 | mem, err := readSize(pid, raw, unsafe.Sizeof(ioevent{})) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | var rawIoCB iocb 34 | if err := decodeStruct(mem, &rawIoCB); err != nil { 35 | return err 36 | } 37 | 38 | arg.t = ArgTypeObject 39 | arg.obj = convertIoCB(rawIoCB) 40 | } else { 41 | arg.annotation = "NULL" 42 | arg.replace = true 43 | } 44 | return nil 45 | }) 46 | } 47 | 48 | func convertIoCB(cb iocb) *Object { 49 | return &Object{ 50 | Name: "iocb", 51 | Properties: []Arg{ 52 | { 53 | name: "data", 54 | t: ArgTypeUnsignedLong, 55 | raw: uintptr(cb.Data), 56 | }, 57 | { 58 | name: "key", 59 | t: ArgTypeUnsignedInt, 60 | raw: uintptr(cb.Key), 61 | }, 62 | { 63 | name: "opcode", 64 | t: ArgTypeUnsignedInt, 65 | raw: uintptr(cb.Opcode), 66 | }, 67 | { 68 | name: "priority", 69 | t: ArgTypeUnsignedInt, 70 | raw: uintptr(cb.Priority), 71 | }, 72 | { 73 | name: "flags", 74 | t: ArgTypeUnsignedInt, 75 | raw: uintptr(cb.Flags), 76 | }, 77 | { 78 | name: "fd", 79 | t: ArgTypeUnsignedInt, 80 | raw: uintptr(cb.Fd), 81 | }, 82 | { 83 | name: "offset", 84 | t: ArgTypeUnsignedLong, 85 | raw: uintptr(cb.Offset), 86 | }, 87 | { 88 | name: "addr", 89 | t: ArgTypeUnsignedLong, 90 | raw: uintptr(cb.Addr), 91 | }, 92 | { 93 | name: "len", 94 | t: ArgTypeUnsignedInt, 95 | raw: uintptr(cb.Len), 96 | }, 97 | { 98 | name: "pos", 99 | t: ArgTypeUnsignedLong, 100 | raw: uintptr(cb.Pos), 101 | }, 102 | { 103 | name: "reserved2", 104 | t: ArgTypeUnsignedInt, 105 | raw: uintptr(cb.Reserved2), 106 | }, 107 | { 108 | name: "reserved3", 109 | t: ArgTypeUnsignedLong, 110 | raw: uintptr(cb.Reserved3), 111 | }, 112 | }, 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /tracer/types_ioevents.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | type ioevent struct { 9 | Data uint64 10 | Obj uint64 11 | Res int64 12 | Res2 int64 13 | } 14 | 15 | func init() { 16 | registerTypeHandler(argTypeIoEvents, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 17 | 18 | if raw > 0 { 19 | 20 | var events []Arg 21 | 22 | // ret contains number of events read 23 | if ret > 0 { 24 | 25 | // read the raw C struct from the process memory 26 | mem, err := readSize(pid, raw, unsafe.Sizeof(ioevent{})*ret) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | var rawEvents []ioevent 32 | if err := decodeAnonymous(reflect.ValueOf(&rawEvents).Elem(), mem); err != nil { 33 | return err 34 | } 35 | 36 | events = convertIoEvents(rawEvents) 37 | } 38 | 39 | arg.t = ArgTypeArray 40 | arg.array = events 41 | } else { 42 | arg.annotation = "NULL" 43 | arg.replace = true 44 | } 45 | return nil 46 | }) 47 | registerTypeHandler(argTypeIoEvent, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 48 | 49 | if raw > 0 { 50 | 51 | // read the raw C struct from the process memory 52 | mem, err := readSize(pid, raw, unsafe.Sizeof(ioevent{})) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | var rawEvent ioevent 58 | if err := decodeStruct(mem, &rawEvent); err != nil { 59 | return err 60 | } 61 | 62 | arg.t = ArgTypeObject 63 | arg.obj = convertIoEvent(rawEvent) 64 | } else { 65 | arg.annotation = "NULL" 66 | arg.replace = true 67 | } 68 | return nil 69 | }) 70 | } 71 | 72 | func convertIoEvents(events []ioevent) []Arg { 73 | var output []Arg 74 | for _, event := range events { 75 | output = append(output, Arg{ 76 | t: ArgTypeObject, 77 | obj: convertIoEvent(event), 78 | }) 79 | } 80 | return output 81 | } 82 | 83 | func convertIoEvent(event ioevent) *Object { 84 | return &Object{ 85 | Name: "io_event", 86 | Properties: []Arg{ 87 | { 88 | name: "data", 89 | t: ArgTypeUnsignedLong, 90 | raw: uintptr(event.Data), 91 | }, 92 | { 93 | name: "obj", 94 | t: ArgTypeUnsignedLong, 95 | raw: uintptr(event.Obj), 96 | }, 97 | { 98 | name: "res", 99 | t: ArgTypeLong, 100 | raw: uintptr(event.Res), 101 | }, 102 | { 103 | name: "res2", 104 | t: ArgTypeLong, 105 | raw: uintptr(event.Res2), 106 | }, 107 | }, 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /tracer/types_iouring.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type iouringParams struct { 8 | SqEntries uint32 9 | CqEntries uint32 10 | Flags uint32 11 | SqThreadCpu uint32 12 | SqThreadIdle uint32 13 | Features uint32 14 | WqFd uint32 15 | } 16 | 17 | func init() { 18 | registerTypeHandler(argTypeIoUringParams, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 19 | 20 | if raw > 0 { 21 | // read the raw C struct from the process memory 22 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(iouringParams{})) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | var params iouringParams 28 | if err := decodeStruct(rawTimeVal, ¶ms); err != nil { 29 | return err 30 | } 31 | 32 | arg.obj = convertIoUringParams(params) 33 | arg.t = ArgTypeObject 34 | } else { 35 | arg.annotation = "NULL" 36 | arg.replace = true 37 | } 38 | return nil 39 | }) 40 | } 41 | 42 | func convertIoUringParams(u iouringParams) *Object { 43 | return &Object{ 44 | Name: "io_uring_params", 45 | Properties: []Arg{ 46 | { 47 | name: "sq_entries", 48 | t: ArgTypeUnsignedLong, 49 | raw: uintptr(u.SqEntries), 50 | }, 51 | { 52 | name: "cq_entries", 53 | t: ArgTypeUnsignedLong, 54 | raw: uintptr(u.CqEntries), 55 | }, 56 | { 57 | name: "flags", 58 | t: ArgTypeUnsignedLong, 59 | raw: uintptr(u.Flags), 60 | }, 61 | { 62 | name: "sq_thread_cpu", 63 | t: ArgTypeUnsignedLong, 64 | raw: uintptr(u.SqThreadCpu), 65 | }, 66 | { 67 | name: "sq_thread_idle", 68 | t: ArgTypeUnsignedLong, 69 | raw: uintptr(u.SqThreadIdle), 70 | }, 71 | { 72 | name: "features", 73 | t: ArgTypeUnsignedLong, 74 | raw: uintptr(u.Features), 75 | }, 76 | { 77 | name: "wq_fd", 78 | t: ArgTypeUnsignedLong, 79 | raw: uintptr(u.WqFd), 80 | }, 81 | }, 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /tracer/types_iovec.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | func init() { 9 | registerTypeHandler(argTypeIovecArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 10 | // read the raw C struct from the process memory 11 | mem, err := readSize(pid, raw, next*unsafe.Sizeof(iovec{})) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | iovecs := make([]iovec, next) 17 | if err := decodeAnonymous(reflect.ValueOf(&iovecs).Elem(), mem); err != nil { 18 | return err 19 | } 20 | 21 | arg.array, err = convertIovecs(iovecs, pid) 22 | if err != nil { 23 | return err 24 | } 25 | arg.t = ArgTypeArray 26 | return nil 27 | }) 28 | } 29 | 30 | type iovec struct { 31 | Base uintptr /* Starting address */ 32 | Len uintptr /* Number of bytes to transfer */ 33 | } 34 | 35 | func convertIovecs(vecs []iovec, pid int) ([]Arg, error) { 36 | var output []Arg 37 | for _, fd := range vecs { 38 | vec, err := convertIovec(fd, pid) 39 | if err != nil { 40 | return nil, err 41 | } 42 | output = append(output, *vec) 43 | } 44 | return output, nil 45 | } 46 | 47 | func convertIovec(vec iovec, pid int) (*Arg, error) { 48 | 49 | base, err := readSize(pid, vec.Base, uintptr(vec.Len)) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return &Arg{ 55 | t: ArgTypeObject, 56 | obj: &Object{ 57 | Name: "iovec", 58 | Properties: []Arg{ 59 | { 60 | name: "base", 61 | t: ArgTypeData, 62 | data: base, 63 | raw: vec.Base, 64 | }, 65 | { 66 | name: "len", 67 | t: ArgTypeUnsignedInt, 68 | raw: vec.Len, 69 | }, 70 | }, 71 | }, 72 | known: true, 73 | }, nil 74 | } 75 | -------------------------------------------------------------------------------- /tracer/types_itimerspec.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeItimerspec, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | // read the raw C struct from the process memory 14 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.ItimerSpec{})) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | var timeVal unix.ItimerSpec 20 | if err := decodeStruct(rawTimeVal, &timeVal); err != nil { 21 | return err 22 | } 23 | 24 | arg.obj = convertITimerSpec(&timeVal) 25 | arg.t = ArgTypeObject 26 | } else { 27 | arg.annotation = "NULL" 28 | arg.replace = true 29 | } 30 | return nil 31 | }) 32 | } 33 | 34 | func convertITimerSpec(u *unix.ItimerSpec) *Object { 35 | if u == nil { 36 | return nil 37 | } 38 | return &Object{ 39 | Name: "itimerspec", 40 | Properties: []Arg{ 41 | { 42 | name: "interval", 43 | t: ArgTypeObject, 44 | obj: convertTimeSpec(&u.Interval), 45 | }, 46 | { 47 | name: "value", 48 | t: ArgTypeObject, 49 | obj: convertTimeSpec(&u.Value), 50 | }, 51 | }, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tracer/types_itimerval.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeItimerval, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | // read the raw C struct from the process memory 14 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Itimerval{})) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | var timeVal unix.Itimerval 20 | if err := decodeStruct(rawTimeVal, &timeVal); err != nil { 21 | return err 22 | } 23 | 24 | arg.obj = convertITimerVal(&timeVal) 25 | arg.t = ArgTypeObject 26 | } else { 27 | arg.annotation = "NULL" 28 | arg.replace = true 29 | } 30 | return nil 31 | }) 32 | } 33 | 34 | func convertITimerVal(u *unix.Itimerval) *Object { 35 | return &Object{ 36 | Name: "itimerval", 37 | Properties: []Arg{ 38 | { 39 | name: "interval", 40 | t: ArgTypeObject, 41 | obj: convertTimeVal(&u.Interval), 42 | }, 43 | { 44 | name: "value", 45 | t: ArgTypeObject, 46 | obj: convertTimeVal(&u.Value), 47 | }, 48 | }, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tracer/types_landlock.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | // used by clone3 syscall 8 | type landlockRulesetAttr struct { 9 | HandledAccessFS uint64 10 | } 11 | 12 | func init() { 13 | registerTypeHandler(argTypeLandlockRulesetAttr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 14 | 15 | if raw > 0 { 16 | // read the raw C struct from the process memory 17 | mem, err := readSize(pid, raw, unsafe.Sizeof(landlockRulesetAttr{})) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | var attr landlockRulesetAttr 23 | if err := decodeStruct(mem, &attr); err != nil { 24 | return err 25 | } 26 | 27 | arg.obj = convertLandlockRulesetAttr(attr) 28 | arg.t = ArgTypeObject 29 | } else { 30 | arg.annotation = "NULL" 31 | arg.replace = true 32 | } 33 | return nil 34 | }) 35 | } 36 | 37 | func convertLandlockRulesetAttr(attr landlockRulesetAttr) *Object { 38 | return &Object{ 39 | Name: "landlock_ruleset_attr", 40 | Properties: []Arg{ 41 | { 42 | name: "handled_access_fs", 43 | t: ArgTypeUnsignedLong, 44 | raw: uintptr(attr.HandledAccessFS), 45 | }, 46 | }, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tracer/types_mountattr.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | // used by clone3 syscall 8 | type mountAttr struct { 9 | Set uint64 10 | Clear uint64 11 | Propagation uint64 12 | UserNamespaceFd uint64 13 | } 14 | 15 | func init() { 16 | registerTypeHandler(argTypeMountAttr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 17 | 18 | if raw > 0 { 19 | // read the raw C struct from the process memory 20 | mem, err := readSize(pid, raw, unsafe.Sizeof(mountAttr{})) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | var attr mountAttr 26 | if err := decodeStruct(mem, &attr); err != nil { 27 | return err 28 | } 29 | 30 | arg.obj = convertMountAttr(attr) 31 | arg.t = ArgTypeObject 32 | } else { 33 | arg.annotation = "NULL" 34 | arg.replace = true 35 | } 36 | return nil 37 | }) 38 | } 39 | 40 | func convertMountAttr(attr mountAttr) *Object { 41 | return &Object{ 42 | Name: "mount_attr", 43 | Properties: []Arg{ 44 | { 45 | name: "set", 46 | t: ArgTypeUnsignedLong, 47 | raw: uintptr(attr.Set), 48 | }, 49 | { 50 | name: "clear", 51 | t: ArgTypeUnsignedLong, 52 | raw: uintptr(attr.Clear), 53 | }, 54 | { 55 | name: "propagation", 56 | t: ArgTypeUnsignedLong, 57 | raw: uintptr(attr.Propagation), 58 | }, 59 | { 60 | name: "user_namespace_fd", 61 | t: ArgTypeUnsignedLong, 62 | raw: uintptr(attr.UserNamespaceFd), 63 | }, 64 | }, 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tracer/types_mqattr.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type mqattr struct { 8 | Flags int32 9 | Maxmsg int32 10 | Msgsize int32 11 | Curmsgs int32 12 | } 13 | 14 | func init() { 15 | registerTypeHandler(argTypeMqAttr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 16 | 17 | if raw > 0 { 18 | // read the raw C struct from the process memory 19 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(mqattr{})) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | var attr mqattr 25 | if err := decodeStruct(rawTimeVal, &attr); err != nil { 26 | return err 27 | } 28 | 29 | arg.obj = convertMqAttr(attr) 30 | arg.t = ArgTypeObject 31 | } else { 32 | arg.annotation = "NULL" 33 | arg.replace = true 34 | } 35 | return nil 36 | }) 37 | } 38 | 39 | func convertMqAttr(a mqattr) *Object { 40 | return &Object{ 41 | Name: "mq_attr", 42 | Properties: []Arg{ 43 | { 44 | name: "flags", 45 | t: ArgTypeUnsignedLong, 46 | raw: uintptr(a.Flags), 47 | }, 48 | { 49 | name: "maxmsg", 50 | t: ArgTypeUnsignedLong, 51 | raw: uintptr(a.Maxmsg), 52 | }, 53 | { 54 | name: "msgsize", 55 | t: ArgTypeUnsignedLong, 56 | raw: uintptr(a.Msgsize), 57 | }, 58 | { 59 | name: "curmsgs", 60 | t: ArgTypeUnsignedLong, 61 | raw: uintptr(a.Curmsgs), 62 | }, 63 | }, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tracer/types_openhow.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "github.com/liamg/grace/tracer/annotation" 7 | ) 8 | 9 | // used by clone3 syscall 10 | type openHow struct { 11 | Flags uint64 12 | Mode uint64 13 | Resolve uint64 14 | } 15 | 16 | func init() { 17 | registerTypeHandler(argTypeOpenHow, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 18 | 19 | if raw > 0 { 20 | // read the raw C struct from the process memory 21 | mem, err := readSize(pid, raw, unsafe.Sizeof(openHow{})) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | var how openHow 27 | if err := decodeStruct(mem, &how); err != nil { 28 | return err 29 | } 30 | 31 | arg.obj = convertOpenHow(how) 32 | arg.t = ArgTypeObject 33 | } else { 34 | arg.annotation = "NULL" 35 | arg.replace = true 36 | } 37 | return nil 38 | }) 39 | } 40 | 41 | func convertOpenHow(how openHow) *Object { 42 | 43 | flags := Arg{ 44 | name: "flags", 45 | t: ArgTypeUnsignedLong, 46 | raw: uintptr(how.Flags), 47 | } 48 | annotation.AnnotateOpenFlags(&flags, 0) 49 | 50 | mode := Arg{ 51 | name: "flags", 52 | t: ArgTypeUnsignedLong, 53 | raw: uintptr(how.Mode), 54 | } 55 | annotation.AnnotateAccMode(&mode, 0) 56 | 57 | resolve := Arg{ 58 | name: "resolve", 59 | t: ArgTypeUnsignedLong, 60 | raw: uintptr(how.Resolve), 61 | } 62 | annotation.AnnotateResolveFlags(&mode, 0) 63 | 64 | return &Object{ 65 | Name: "open_how", 66 | Properties: []Arg{ 67 | flags, 68 | mode, 69 | resolve, 70 | }, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tracer/types_pollfd.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | func init() { 9 | registerTypeHandler(argTypePollFdArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 10 | if raw > 0 { 11 | // read the raw C struct from the process memory 12 | rawPollFds, err := readSize(pid, raw, next*unsafe.Sizeof(pollfd{})) 13 | if err != nil { 14 | return err 15 | } 16 | 17 | pollFds := make([]pollfd, next) 18 | if err := decodeAnonymous(reflect.ValueOf(&pollFds).Elem(), rawPollFds); err != nil { 19 | return err 20 | } 21 | 22 | arg.array = convertPollFds(pollFds) 23 | } 24 | arg.t = ArgTypeArray 25 | return nil 26 | }) 27 | } 28 | 29 | type pollfd struct { 30 | Fd int /* file descriptor */ 31 | Events uint16 /* events requested for polling */ 32 | REvents uint32 /* events that occurred during polling */ 33 | } 34 | 35 | func convertPollFds(fds []pollfd) []Arg { 36 | var output []Arg 37 | for _, fd := range fds { 38 | output = append(output, convertPollFd(fd)) 39 | } 40 | return output 41 | } 42 | 43 | func convertPollFd(fd pollfd) Arg { 44 | return Arg{ 45 | t: ArgTypeObject, 46 | obj: &Object{ 47 | Name: "pollfd", 48 | Properties: []Arg{ 49 | { 50 | name: "fd", 51 | t: ArgTypeInt, 52 | raw: uintptr(fd.Fd), 53 | }, 54 | { 55 | name: "events", 56 | t: ArgTypeInt, 57 | raw: uintptr(fd.Events), 58 | }, 59 | { 60 | name: "revents", 61 | t: ArgTypeInt, 62 | raw: uintptr(fd.REvents), 63 | }, 64 | }, 65 | }, 66 | known: true, 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tracer/types_rlimit.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | func init() { 9 | registerTypeHandler(argTypeRLimit, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 10 | if raw > 0 { 11 | data, err := readSize(pid, raw, unsafe.Sizeof(syscall.Rlimit{})) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | var rlimit syscall.Rlimit 17 | if err := decodeStruct(data, &rlimit); err != nil { 18 | return err 19 | } 20 | 21 | arg.obj = convertRLimit(&rlimit) 22 | arg.t = ArgTypeObject 23 | } else { 24 | arg.t = ArgTypeAddress 25 | arg.annotation = "NULL" 26 | arg.replace = true 27 | } 28 | return nil 29 | }) 30 | } 31 | 32 | func convertRLimit(rlimit *syscall.Rlimit) *Object { 33 | return &Object{ 34 | Name: "rlimit", 35 | Properties: []Arg{ 36 | { 37 | name: "cur", 38 | t: ArgTypeUnsignedLong, 39 | raw: uintptr(rlimit.Cur), 40 | }, 41 | { 42 | name: "max", 43 | t: ArgTypeUnsignedLong, 44 | raw: uintptr(rlimit.Max), 45 | }, 46 | }, 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tracer/types_rusage.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | func init() { 9 | registerTypeHandler(argTypeRUsage, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 10 | if raw > 0 { 11 | data, err := readSize(pid, raw, unsafe.Sizeof(syscall.Rusage{})) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | var rusage syscall.Rusage 17 | if err := decodeStruct(data, &rusage); err != nil { 18 | return err 19 | } 20 | 21 | arg.obj = convertRUsage(&rusage) 22 | arg.t = ArgTypeObject 23 | } else { 24 | arg.t = ArgTypeAddress 25 | arg.annotation = "NULL" 26 | arg.replace = true 27 | } 28 | return nil 29 | }) 30 | } 31 | 32 | func convertRUsage(rusage *syscall.Rusage) *Object { 33 | return &Object{ 34 | Name: "rusage", 35 | Properties: []Arg{ 36 | { 37 | name: "utime", 38 | t: ArgTypeObject, 39 | obj: &Object{ 40 | Name: "timeval", 41 | Properties: []Arg{ 42 | { 43 | name: "tv_sec", 44 | t: ArgTypeLong, 45 | raw: uintptr(rusage.Utime.Sec), 46 | }, 47 | { 48 | name: "tv_usec", 49 | t: ArgTypeLong, 50 | raw: uintptr(rusage.Utime.Usec), 51 | }, 52 | }, 53 | }, 54 | }, 55 | { 56 | name: "stime", 57 | t: ArgTypeObject, 58 | obj: &Object{ 59 | Name: "timeval", 60 | Properties: []Arg{ 61 | { 62 | name: "tv_sec", 63 | t: ArgTypeLong, 64 | raw: uintptr(rusage.Stime.Sec), 65 | }, 66 | { 67 | name: "tv_usec", 68 | t: ArgTypeLong, 69 | raw: uintptr(rusage.Stime.Usec), 70 | }, 71 | }, 72 | }, 73 | }, 74 | { 75 | name: "maxrss", 76 | t: ArgTypeLong, 77 | raw: uintptr(rusage.Maxrss), 78 | }, 79 | { 80 | name: "ixrss", 81 | t: ArgTypeLong, 82 | raw: uintptr(rusage.Ixrss), 83 | }, 84 | { 85 | name: "idrss", 86 | t: ArgTypeLong, 87 | raw: uintptr(rusage.Idrss), 88 | }, 89 | { 90 | name: "isrss", 91 | t: ArgTypeLong, 92 | raw: uintptr(rusage.Isrss), 93 | }, 94 | { 95 | name: "minflt", 96 | t: ArgTypeLong, 97 | raw: uintptr(rusage.Minflt), 98 | }, 99 | { 100 | name: "majflt", 101 | t: ArgTypeLong, 102 | raw: uintptr(rusage.Majflt), 103 | }, 104 | { 105 | name: "nswap", 106 | t: ArgTypeLong, 107 | raw: uintptr(rusage.Nswap), 108 | }, 109 | { 110 | name: "inblock", 111 | t: ArgTypeLong, 112 | raw: uintptr(rusage.Inblock), 113 | }, 114 | { 115 | name: "oublock", 116 | t: ArgTypeLong, 117 | raw: uintptr(rusage.Oublock), 118 | }, 119 | { 120 | name: "msgsnd", 121 | t: ArgTypeLong, 122 | raw: uintptr(rusage.Msgsnd), 123 | }, 124 | { 125 | name: "msgrcv", 126 | t: ArgTypeLong, 127 | raw: uintptr(rusage.Msgrcv), 128 | }, 129 | { 130 | name: "nsignals", 131 | t: ArgTypeLong, 132 | raw: uintptr(rusage.Nsignals), 133 | }, 134 | { 135 | name: "nvcsw", 136 | t: ArgTypeLong, 137 | raw: uintptr(rusage.Nvcsw), 138 | }, 139 | { 140 | name: "nivcsw", 141 | t: ArgTypeLong, 142 | raw: uintptr(rusage.Nivcsw), 143 | }, 144 | }, 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /tracer/types_schedattr.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type schedAttr struct { 8 | Size uint32 9 | SchedPolicy uint32 10 | SchedFlags uint64 11 | SchedNice int32 12 | SchedPriority uint32 13 | SchedRuntime uint64 14 | SchedDeadline uint64 15 | SchedPeriod uint64 16 | } 17 | 18 | func init() { 19 | registerTypeHandler(argTypeSchedAttr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 20 | 21 | if raw > 0 { 22 | rawVal, err := readSize(pid, raw, unsafe.Sizeof(schedAttr{})) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | var attr schedAttr 28 | if err := decodeStruct(rawVal, &attr); err != nil { 29 | return err 30 | } 31 | 32 | arg.obj = convertSchedAttr(attr) 33 | if err != nil { 34 | return err 35 | } 36 | arg.t = ArgTypeObject 37 | } else { 38 | arg.annotation = "NULL" 39 | arg.replace = true 40 | } 41 | return nil 42 | }) 43 | } 44 | 45 | func convertSchedAttr(param schedAttr) *Object { 46 | return &Object{ 47 | Name: "sched_attr", 48 | Properties: []Arg{ 49 | { 50 | name: "size", 51 | t: ArgTypeInt, 52 | raw: uintptr(param.Size), 53 | }, 54 | { 55 | name: "sched_policy", 56 | t: ArgTypeInt, 57 | raw: uintptr(param.SchedPolicy), 58 | }, 59 | { 60 | name: "sched_flags", 61 | t: ArgTypeInt, 62 | raw: uintptr(param.SchedFlags), 63 | }, 64 | { 65 | name: "sched_nice", 66 | t: ArgTypeInt, 67 | raw: uintptr(param.SchedNice), 68 | }, 69 | { 70 | name: "sched_priority", 71 | t: ArgTypeInt, 72 | raw: uintptr(param.SchedPriority), 73 | }, 74 | { 75 | name: "sched_runtime", 76 | t: ArgTypeInt, 77 | raw: uintptr(param.SchedRuntime), 78 | }, 79 | { 80 | name: "sched_deadline", 81 | t: ArgTypeInt, 82 | raw: uintptr(param.SchedDeadline), 83 | }, 84 | { 85 | name: "sched_period", 86 | t: ArgTypeInt, 87 | raw: uintptr(param.SchedPeriod), 88 | }, 89 | }, 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tracer/types_schedparam.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type schedParam struct { 8 | Priority int32 9 | } 10 | 11 | func init() { 12 | registerTypeHandler(argTypeSchedParam, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 13 | 14 | if raw > 0 { 15 | rawVal, err := readSize(pid, raw, unsafe.Sizeof(schedParam{})) 16 | if err != nil { 17 | return err 18 | } 19 | 20 | var param schedParam 21 | if err := decodeStruct(rawVal, ¶m); err != nil { 22 | return err 23 | } 24 | 25 | arg.obj = convertSchedParam(param) 26 | if err != nil { 27 | return err 28 | } 29 | arg.t = ArgTypeObject 30 | } else { 31 | arg.annotation = "NULL" 32 | arg.replace = true 33 | } 34 | return nil 35 | }) 36 | } 37 | 38 | func convertSchedParam(param schedParam) *Object { 39 | return &Object{ 40 | Name: "sched_param", 41 | Properties: []Arg{ 42 | { 43 | name: "priority", 44 | t: ArgTypeInt, 45 | raw: uintptr(param.Priority), 46 | }, 47 | }, 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tracer/types_sembuf.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | 7 | "github.com/liamg/grace/tracer/annotation" 8 | ) 9 | 10 | type sembuf struct { 11 | Num uint16 /* semaphore index in array */ 12 | Op int16 /* semaphore operation */ 13 | Flg int16 /* flags for operation */ 14 | } 15 | 16 | func init() { 17 | registerTypeHandler(argTypeSembuf, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 18 | // read the raw C struct from the process memory 19 | mem, err := readSize(pid, raw, next*unsafe.Sizeof(sembuf{})*next) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | sembufs := make([]sembuf, next) 25 | if err := decodeAnonymous(reflect.ValueOf(&sembufs).Elem(), mem); err != nil { 26 | return err 27 | } 28 | 29 | arg.array, err = convertSembufs(sembufs, pid) 30 | if err != nil { 31 | return err 32 | } 33 | arg.t = ArgTypeArray 34 | return nil 35 | }) 36 | } 37 | 38 | func convertSembufs(bufs []sembuf, pid int) ([]Arg, error) { 39 | var items []Arg 40 | for _, buf := range bufs { 41 | obj := convertSembuf(buf, pid) 42 | items = append(items, obj) 43 | } 44 | return items, nil 45 | } 46 | 47 | func convertSembuf(buf sembuf, pid int) Arg { 48 | 49 | flagsArg := Arg{ 50 | name: "flags", 51 | t: ArgTypeInt, 52 | raw: uintptr(buf.Flg), 53 | } 54 | annotation.AnnotateSemFlags(&flagsArg, pid) 55 | 56 | return Arg{ 57 | name: "sops", 58 | t: ArgTypeObject, 59 | obj: &Object{ 60 | Name: "sembuf", 61 | Properties: []Arg{ 62 | { 63 | name: "sem_num", 64 | t: ArgTypeInt, 65 | raw: uintptr(buf.Num), 66 | }, 67 | { 68 | name: "sem_op", 69 | t: ArgTypeInt, 70 | raw: uintptr(buf.Op), 71 | }, 72 | flagsArg, 73 | }, 74 | }, 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /tracer/types_shmidds.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type shmidds struct { 8 | IPCPerm struct { 9 | Key uint32 /* Key provided to shmget */ 10 | Uid uint32 /* Effective UID of owner */ 11 | Gid uint32 /* Effective GID of owner */ 12 | Cuid uint32 /* Effective UID of creator */ 13 | Cgid uint32 /* Effective GID of creator */ 14 | Mode uint32 /* Permissions and SHM_DEST + SHM_LOCKED flags */ 15 | Pad1 uint16 16 | Seq uint16 /* Sequence */ 17 | Pad2 uint16 18 | Unused1 uint 19 | Unused2 uint 20 | } /* Ownership and permissions */ 21 | Segsz uint32 /* Size of shared segment (bytes) */ 22 | Atime uint64 /* Last attach time */ 23 | Dtime uint64 /* Last detach time */ 24 | Ctime uint64 /* Last change time */ 25 | Cpid uint32 /* PID of shared segment creator */ 26 | Lpid uint32 /* PID of last shmat(2)/shmdt(2) syscall */ 27 | Nattch uint16 /* Number of current attaches */ 28 | Unused uint16 29 | Unused2 uintptr 30 | Unused3 uintptr 31 | } 32 | 33 | func init() { 34 | registerTypeHandler(argTypeSHMIDDS, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 35 | 36 | if raw > 0 { 37 | 38 | // read the raw C struct from the process memory 39 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(shmidds{})) 40 | if err != nil { 41 | return err 42 | } 43 | 44 | var ds shmidds 45 | if err := decodeStruct(rawTimeVal, &ds); err != nil { 46 | return err 47 | } 48 | 49 | arg.obj = convertSHMIDDS(&ds) 50 | arg.t = ArgTypeObject 51 | } else { 52 | arg.annotation = "NULL" 53 | arg.replace = true 54 | } 55 | return nil 56 | }) 57 | } 58 | 59 | func convertSHMIDDS(ds *shmidds) *Object { 60 | 61 | var permProps []Arg 62 | 63 | permProps = append(permProps, Arg{ 64 | name: "key", 65 | t: ArgTypeUnsignedInt, 66 | raw: uintptr(ds.IPCPerm.Key), 67 | }) 68 | permProps = append(permProps, Arg{ 69 | name: "uid", 70 | t: ArgTypeUnsignedInt, 71 | raw: uintptr(ds.IPCPerm.Uid), 72 | }) 73 | permProps = append(permProps, Arg{ 74 | name: "gid", 75 | t: ArgTypeUnsignedInt, 76 | raw: uintptr(ds.IPCPerm.Gid), 77 | }) 78 | permProps = append(permProps, Arg{ 79 | name: "cuid", 80 | t: ArgTypeUnsignedInt, 81 | raw: uintptr(ds.IPCPerm.Cuid), 82 | }) 83 | permProps = append(permProps, Arg{ 84 | name: "cgid", 85 | t: ArgTypeUnsignedInt, 86 | raw: uintptr(ds.IPCPerm.Cgid), 87 | }) 88 | permProps = append(permProps, Arg{ 89 | name: "mode", 90 | t: ArgTypeUnsignedInt, 91 | raw: uintptr(ds.IPCPerm.Mode), 92 | }) 93 | permProps = append(permProps, Arg{ 94 | name: "seq", 95 | t: ArgTypeUnsignedInt, 96 | raw: uintptr(ds.IPCPerm.Seq), 97 | }) 98 | 99 | var props []Arg 100 | 101 | props = append(props, Arg{ 102 | name: "perm", 103 | t: ArgTypeObject, 104 | obj: &Object{ 105 | Name: "ipc_perm", 106 | }, 107 | }) 108 | props = append(props, Arg{ 109 | name: "segsize", 110 | t: ArgTypeUnsignedInt, 111 | raw: uintptr(ds.Segsz), 112 | }) 113 | 114 | // TODO: read time structs from process memory instead of storing raw addresses 115 | 116 | props = append(props, Arg{ 117 | name: "atime", 118 | t: ArgTypeAddress, 119 | raw: uintptr(ds.Atime), 120 | }) 121 | props = append(props, Arg{ 122 | name: "dtime", 123 | t: ArgTypeAddress, 124 | raw: uintptr(ds.Dtime), 125 | }) 126 | props = append(props, Arg{ 127 | name: "ctime", 128 | t: ArgTypeAddress, 129 | raw: uintptr(ds.Ctime), 130 | }) 131 | 132 | props = append(props, Arg{ 133 | name: "cpid", 134 | t: ArgTypeUnsignedInt, 135 | raw: uintptr(ds.Cpid), 136 | }) 137 | props = append(props, Arg{ 138 | name: "lpid", 139 | t: ArgTypeUnsignedInt, 140 | raw: uintptr(ds.Lpid), 141 | }) 142 | props = append(props, Arg{ 143 | name: "nattch", 144 | t: ArgTypeUnsignedInt, 145 | raw: uintptr(ds.Nattch), 146 | }) 147 | 148 | return &Object{ 149 | Name: "shmid_ds", 150 | Properties: props, 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /tracer/types_sigaction.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "strings" 5 | "unsafe" 6 | ) 7 | 8 | /* 9 | #include 10 | 11 | const void* sig_dfl = SIG_DFL; 12 | const void* sig_ign = SIG_IGN; 13 | */ 14 | import "C" 15 | 16 | type sigAction struct { 17 | Handler uintptr 18 | Sigaction uintptr 19 | Mask int 20 | Flags int 21 | Restorer uintptr 22 | } 23 | 24 | func init() { 25 | registerTypeHandler(argTypeSigAction, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 26 | if raw > 0 { 27 | 28 | raw, err := readSize(pid, raw, unsafe.Sizeof(sigAction{})) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | var action sigAction 34 | if err := decodeStruct(raw, &action); err != nil { 35 | return err 36 | } 37 | 38 | arg.obj = convertSigAction(&action) 39 | arg.t = ArgTypeObject 40 | } else { 41 | arg.annotation = "NULL" 42 | arg.replace = true 43 | } 44 | return nil 45 | }) 46 | } 47 | 48 | func convertSigAction(action *sigAction) *Object { 49 | obj := Object{ 50 | Name: "sigaction", 51 | } 52 | 53 | var handlerStr string 54 | switch action.Handler { 55 | case uintptr(C.sig_dfl): 56 | handlerStr = "SIG_DFL" 57 | case uintptr(C.sig_ign): 58 | handlerStr = "SIG_IGN" 59 | } 60 | obj.Properties = append(obj.Properties, Arg{ 61 | name: "handler", 62 | t: ArgTypeAddress, 63 | raw: action.Handler, 64 | annotation: handlerStr, 65 | replace: handlerStr != "", 66 | }) 67 | 68 | obj.Properties = append(obj.Properties, Arg{ 69 | name: "sigaction", 70 | t: ArgTypeAddress, 71 | raw: action.Sigaction, 72 | }) 73 | obj.Properties = append(obj.Properties, Arg{ 74 | name: "mask", 75 | t: ArgTypeInt, 76 | raw: uintptr(action.Mask), 77 | }) 78 | 79 | var signActionFlags []string 80 | if action.Flags&C.SA_NOCLDSTOP != 0 { 81 | signActionFlags = append(signActionFlags, "SA_NOCLDSTOP") 82 | } 83 | if action.Flags&C.SA_NOCLDWAIT != 0 { 84 | signActionFlags = append(signActionFlags, "SA_NOCLDWAIT") 85 | } 86 | if action.Flags&C.SA_NODEFER != 0 { 87 | signActionFlags = append(signActionFlags, "SA_NODEFER") 88 | } 89 | if action.Flags&C.SA_ONSTACK != 0 { 90 | signActionFlags = append(signActionFlags, "SA_ONSTACK") 91 | } 92 | if action.Flags&C.SA_RESETHAND != 0 { 93 | signActionFlags = append(signActionFlags, "SA_RESETHAND") 94 | } 95 | if action.Flags&C.SA_RESTART != 0 { 96 | signActionFlags = append(signActionFlags, "SA_RESTART") 97 | } 98 | if action.Flags&C.SA_RESTORER != 0 { 99 | signActionFlags = append(signActionFlags, "SA_RESTORER") 100 | } 101 | if action.Flags&C.SA_SIGINFO != 0 { 102 | signActionFlags = append(signActionFlags, "SA_SIGINFO") 103 | } 104 | if action.Flags&C.SA_UNSUPPORTED != 0 { 105 | signActionFlags = append(signActionFlags, "SA_UNSUPPORTED") 106 | } 107 | if action.Flags&C.SA_EXPOSE_TAGBITS != 0 { 108 | signActionFlags = append(signActionFlags, "SA_EXPOSE_TAGBITS") 109 | } 110 | flagStr := strings.Join(signActionFlags, "|") 111 | 112 | obj.Properties = append(obj.Properties, Arg{ 113 | name: "flags", 114 | t: ArgTypeInt, 115 | raw: uintptr(action.Flags), 116 | annotation: flagStr, 117 | replace: flagStr != "", 118 | }) 119 | obj.Properties = append(obj.Properties, Arg{ 120 | name: "restorer", 121 | t: ArgTypeAddress, 122 | raw: action.Restorer, 123 | }) 124 | 125 | return &obj 126 | } 127 | -------------------------------------------------------------------------------- /tracer/types_siginfo.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | 7 | "github.com/liamg/grace/tracer/annotation" 8 | 9 | "golang.org/x/sys/unix" 10 | ) 11 | 12 | /* 13 | #include 14 | */ 15 | import "C" 16 | 17 | func init() { 18 | registerTypeHandler(argTypeSigInfo, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 19 | if raw > 0 { 20 | 21 | raw, err := readSize(pid, raw, unsafe.Sizeof(unix.Siginfo{})) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | var info unix.Siginfo 27 | if err := decodeStruct(raw, &info); err != nil { 28 | return err 29 | } 30 | 31 | arg.obj = convertSigInfo(&info, pid) 32 | arg.t = ArgTypeObject 33 | } else { 34 | arg.annotation = "NULL" 35 | arg.replace = true 36 | } 37 | return nil 38 | }) 39 | } 40 | 41 | func convertSigInfo(u *unix.Siginfo, pid int) *Object { 42 | 43 | signo := Arg{ 44 | name: "signo", 45 | t: ArgTypeInt, 46 | raw: uintptr(u.Signo), 47 | } 48 | annotation.AnnotateSignal(&signo, pid) 49 | 50 | errStr := annotation.ErrNoToString(int(u.Errno)) 51 | errno := Arg{ 52 | name: "errno", 53 | t: ArgTypeInt, 54 | raw: uintptr(u.Errno), 55 | annotation: errStr, 56 | replace: errStr != "", 57 | } 58 | 59 | codeStr := annotation.SignalCodeToString(syscall.Signal(u.Signo), u.Code) 60 | 61 | code := Arg{ 62 | name: "code", 63 | t: ArgTypeInt, 64 | raw: uintptr(u.Code), 65 | annotation: codeStr, 66 | replace: codeStr != "", 67 | } 68 | 69 | return &Object{ 70 | Name: "siginfo", 71 | Properties: []Arg{ 72 | signo, 73 | errno, 74 | code, 75 | }, 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tracer/types_sockaddr.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | 8 | "github.com/liamg/grace/tracer/annotation" 9 | ) 10 | 11 | func init() { 12 | registerTypeHandler(argTypeSockaddr, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 13 | if raw > 0 { 14 | rawFamily, err := readSize(pid, raw, unsafe.Sizeof(syscall.RawSockaddrInet4{}.Family)) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | family := decodeInt(rawFamily) 20 | 21 | rawSockAddr, err := readSize(pid, raw, next) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | arg.obj, err = convertSockAddr(family, rawSockAddr) 27 | if err != nil { 28 | return err 29 | } 30 | arg.t = ArgTypeObject 31 | } else { 32 | arg.t = ArgTypeAddress 33 | arg.annotation = "NULL" 34 | arg.replace = true 35 | } 36 | return nil 37 | }) 38 | } 39 | 40 | func htons(port uint16) uint16 { 41 | return port>>8 | port<<8 42 | } 43 | 44 | func convertSockAddr(family int64, raw []byte) (*Object, error) { 45 | switch family { 46 | case syscall.AF_INET: 47 | var target syscall.RawSockaddrInet4 48 | if err := decodeStruct(raw, &target); err != nil { 49 | return nil, err 50 | } 51 | return &Object{ 52 | Name: "sockaddr", 53 | Properties: []Arg{ 54 | { 55 | name: "family", 56 | annotation: "AF_INET", 57 | replace: true, 58 | }, 59 | { 60 | name: "port", 61 | t: ArgTypeInt, 62 | raw: uintptr(htons(target.Port)), 63 | }, 64 | { 65 | name: "addr", 66 | t: ArgTypeData, 67 | data: []byte(fmt.Sprintf("%d.%d.%d.%d", target.Addr[0], target.Addr[1], target.Addr[2], target.Addr[3])), 68 | }, 69 | }, 70 | }, nil 71 | case syscall.AF_INET6: 72 | var target syscall.RawSockaddrInet6 73 | if err := decodeStruct(raw, &target); err != nil { 74 | return nil, err 75 | } 76 | return &Object{ 77 | Name: "sockaddr", 78 | Properties: []Arg{ 79 | { 80 | name: "family", 81 | annotation: "AF_INET6", 82 | replace: true, 83 | }, 84 | { 85 | name: "port", 86 | t: ArgTypeInt, 87 | raw: uintptr(htons(target.Port)), 88 | }, 89 | { 90 | name: "addr", 91 | t: ArgTypeData, 92 | data: []byte(fmt.Sprintf("%x:%x:%x:%x:%x:%x:%x:%x", target.Addr[0], target.Addr[1], target.Addr[2], target.Addr[3], target.Addr[4], target.Addr[5], target.Addr[6], target.Addr[7])), 93 | }, 94 | { 95 | name: "flowinfo", 96 | t: ArgTypeInt, 97 | raw: uintptr(target.Flowinfo), 98 | }, 99 | { 100 | name: "scope_id", 101 | t: ArgTypeInt, 102 | raw: uintptr(target.Scope_id), 103 | }, 104 | }, 105 | }, nil 106 | case syscall.AF_UNIX: 107 | var target syscall.RawSockaddrUnix 108 | 109 | if len(raw) < int(unsafe.Sizeof(target)) { 110 | raw = append(raw, make([]byte, int(unsafe.Sizeof(target))-len(raw))...) 111 | } 112 | 113 | if err := decodeStruct(raw, &target); err != nil { 114 | return nil, err 115 | } 116 | var path string 117 | for _, b := range target.Path { 118 | if b == 0 { 119 | break 120 | } 121 | path += string(byte(b)) 122 | } 123 | return &Object{ 124 | Name: "sockaddr", 125 | Properties: []Arg{ 126 | { 127 | name: "family", 128 | annotation: "AF_UNIX", 129 | replace: true, 130 | }, 131 | { 132 | name: "path", 133 | t: ArgTypeData, 134 | data: []byte(path), 135 | }, 136 | }, 137 | }, nil 138 | case syscall.AF_NETLINK: 139 | var target syscall.RawSockaddrNetlink 140 | if err := decodeStruct(raw, &target); err != nil { 141 | return nil, err 142 | } 143 | return &Object{ 144 | Name: "sockaddr", 145 | Properties: []Arg{ 146 | { 147 | name: "family", 148 | annotation: "AF_NETLINK", 149 | replace: true, 150 | }, 151 | { 152 | name: "pid", 153 | t: ArgTypeInt, 154 | raw: uintptr(target.Pid), 155 | }, 156 | { 157 | name: "groups", 158 | t: ArgTypeInt, 159 | raw: uintptr(target.Groups), 160 | }, 161 | }, 162 | }, nil 163 | default: 164 | var target syscall.RawSockaddr 165 | if err := decodeStruct(raw, &target); err != nil { 166 | return nil, err 167 | } 168 | data := make([]byte, len(target.Data)) 169 | for i, b := range target.Data { 170 | data[i] = byte(b) 171 | } 172 | familyStr := annotation.SocketFamily(int(target.Family)) 173 | return &Object{ 174 | Name: "sockaddr", 175 | Properties: []Arg{ 176 | { 177 | name: "family", 178 | annotation: familyStr, 179 | replace: familyStr != "", 180 | }, 181 | { 182 | name: "data", 183 | t: ArgTypeData, 184 | data: data, 185 | }, 186 | }, 187 | }, nil 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /tracer/types_sockoptval.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | func init() { 4 | registerTypeHandler(argTypeSockoptval, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 5 | if raw > 0 { 6 | rawSockOptVal, err := readSize(pid, raw, next) 7 | if err != nil { 8 | return err 9 | } 10 | 11 | switch prev { 12 | // TODO: expect specific types for specific options? 13 | default: 14 | if next == 4 { // if it's exactly 4 bytes, it's probably an int 15 | arg.raw = uintptr(decodeInt(rawSockOptVal)) 16 | arg.t = ArgTypeInt 17 | } else { // otherwise it's a big pile of misc data 18 | arg.data = rawSockOptVal 19 | arg.t = ArgTypeData 20 | } 21 | } 22 | } else { 23 | arg.t = ArgTypeAddress 24 | arg.annotation = "NULL" 25 | arg.replace = true 26 | } 27 | return nil 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /tracer/types_stack.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "github.com/liamg/grace/tracer/annotation" 7 | ) 8 | 9 | type stack struct { 10 | Sp uintptr 11 | Flags uint32 12 | Size uint32 13 | } 14 | 15 | func init() { 16 | registerTypeHandler(argTypeStack, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 17 | 18 | if raw > 0 { 19 | // read the raw C struct from the process memory 20 | mem, err := readSize(pid, raw, unsafe.Sizeof(stack{})) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | var stack stack 26 | if err := decodeStruct(mem, &stack); err != nil { 27 | return err 28 | } 29 | 30 | arg.obj = convertStack(&stack) 31 | arg.t = ArgTypeObject 32 | } else { 33 | arg.annotation = "NULL" 34 | arg.replace = true 35 | } 36 | return nil 37 | }) 38 | } 39 | 40 | func convertStack(st *stack) *Object { 41 | 42 | flagsArg := Arg{ 43 | name: "flags", 44 | t: ArgTypeUnsignedInt, 45 | raw: uintptr(st.Flags), 46 | } 47 | annotation.AnnotateSigStackFlags(&flagsArg, 0) 48 | 49 | return &Object{ 50 | Name: "stack", 51 | Properties: []Arg{ 52 | { 53 | name: "sp", 54 | t: ArgTypeAddress, 55 | raw: st.Sp, 56 | }, 57 | flagsArg, 58 | { 59 | name: "size", 60 | t: ArgTypeUnsignedInt, 61 | raw: uintptr(st.Size), 62 | }, 63 | }, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tracer/types_stat.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "syscall" 7 | "unsafe" 8 | 9 | "github.com/liamg/grace/tracer/annotation" 10 | 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | func init() { 15 | registerTypeHandler(argTypeStat, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 16 | if raw > 0 { 17 | // read the raw C struct from the process memory 18 | rawStat, err := readSize(pid, raw, unsafe.Sizeof(syscall.Stat_t{})) 19 | if err != nil { 20 | return err 21 | } 22 | 23 | var stat syscall.Stat_t 24 | if err := decodeStruct(rawStat, &stat); err != nil { 25 | return err 26 | } 27 | 28 | arg.obj = convertStat(&stat) 29 | arg.t = ArgTypeObject 30 | } else { 31 | arg.annotation = "NULL" 32 | arg.replace = true 33 | } 34 | return nil 35 | }) 36 | } 37 | 38 | func convertStat(stat *syscall.Stat_t) *Object { 39 | obj := Object{ 40 | Name: "stat", 41 | } 42 | 43 | obj.Properties = append(obj.Properties, Arg{ 44 | name: "mode", 45 | t: ArgTypeUnsignedInt, 46 | raw: uintptr(stat.Mode), 47 | annotation: permModeToString(stat.Mode), 48 | replace: true, 49 | }) 50 | 51 | obj.Properties = append(obj.Properties, Arg{ 52 | name: "dev", 53 | t: ArgTypeUnsignedInt, 54 | raw: uintptr(stat.Dev), 55 | annotation: annotation.DeviceToString(stat.Dev), 56 | replace: true, 57 | }) 58 | 59 | obj.Properties = append(obj.Properties, Arg{ 60 | name: "ino", 61 | t: ArgTypeUnsignedInt, 62 | raw: uintptr(stat.Ino), 63 | }) 64 | 65 | obj.Properties = append(obj.Properties, Arg{ 66 | name: "nlink", 67 | t: ArgTypeUnsignedInt, 68 | raw: uintptr(stat.Nlink), 69 | }) 70 | 71 | obj.Properties = append(obj.Properties, Arg{ 72 | name: "uid", 73 | t: ArgTypeUnsignedInt, 74 | raw: uintptr(stat.Uid), 75 | }) 76 | 77 | obj.Properties = append(obj.Properties, Arg{ 78 | name: "gid", 79 | t: ArgTypeUnsignedInt, 80 | raw: uintptr(stat.Gid), 81 | }) 82 | 83 | obj.Properties = append(obj.Properties, Arg{ 84 | name: "blksize", 85 | t: ArgTypeInt, 86 | raw: uintptr(stat.Blksize), 87 | }) 88 | 89 | obj.Properties = append(obj.Properties, Arg{ 90 | name: "blocks", 91 | t: ArgTypeInt, 92 | raw: uintptr(stat.Blocks), 93 | }) 94 | 95 | obj.Properties = append(obj.Properties, Arg{ 96 | name: "size", 97 | t: ArgTypeInt, 98 | raw: uintptr(stat.Size), 99 | }) 100 | 101 | obj.Properties = append(obj.Properties, Arg{ 102 | name: "nlink", 103 | t: ArgTypeUnsignedInt, 104 | raw: uintptr(stat.Nlink), 105 | }) 106 | 107 | obj.Properties = append(obj.Properties, Arg{ 108 | name: "rdev", 109 | t: ArgTypeUnsignedInt, 110 | raw: uintptr(stat.Rdev), 111 | annotation: annotation.DeviceToString(stat.Rdev), 112 | replace: true, 113 | }) 114 | 115 | return &obj 116 | } 117 | 118 | func permModeToString(mode uint32) string { 119 | 120 | flags := map[uint32]string{ 121 | unix.S_IFBLK: "S_IFBLK", 122 | unix.S_IFCHR: "S_IFCHR", 123 | unix.S_IFIFO: "S_IFIFO", 124 | unix.S_IFLNK: "S_IFLNK", 125 | unix.S_IFREG: "S_IFREG", 126 | unix.S_IFDIR: "S_IFDIR", 127 | unix.S_IFSOCK: "S_IFSOCK", 128 | unix.S_ISUID: "S_ISUID", 129 | unix.S_ISGID: "S_ISGID", 130 | unix.S_ISVTX: "S_ISVTX", 131 | } 132 | 133 | var joins []string 134 | for flag, name := range flags { 135 | if mode&syscall.S_IFMT == flag { 136 | joins = append(joins, name) 137 | } 138 | } 139 | 140 | perm := fmt.Sprintf("%04o", int(mode)&0777) 141 | 142 | joins = append(joins, perm) 143 | 144 | return strings.Join(joins, "|") 145 | } 146 | -------------------------------------------------------------------------------- /tracer/types_statfs.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeStatfs, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | rawVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Statfs_t{})) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | var stat unix.Statfs_t 19 | if err := decodeStruct(rawVal, &stat); err != nil { 20 | return err 21 | } 22 | 23 | arg.obj, err = convertStatfs(&stat, pid) 24 | if err != nil { 25 | return err 26 | } 27 | arg.t = ArgTypeObject 28 | } else { 29 | arg.annotation = "NULL" 30 | arg.replace = true 31 | } 32 | return nil 33 | }) 34 | } 35 | 36 | func convertStatfs(stat *unix.Statfs_t, _ int) (*Object, error) { 37 | return &Object{ 38 | Name: "statfs", 39 | Properties: []Arg{ 40 | { 41 | name: "type", 42 | t: ArgTypeUnsignedLong, 43 | raw: uintptr(stat.Type), 44 | }, 45 | { 46 | name: "bsize", 47 | t: ArgTypeUnsignedLong, 48 | raw: uintptr(stat.Bsize), 49 | }, 50 | 51 | { 52 | name: "blocks", 53 | t: ArgTypeUnsignedLong, 54 | raw: uintptr(stat.Blocks), 55 | }, 56 | { 57 | name: "bfree", 58 | t: ArgTypeUnsignedLong, 59 | raw: uintptr(stat.Bfree), 60 | }, 61 | { 62 | name: "bavail", 63 | t: ArgTypeUnsignedLong, 64 | raw: uintptr(stat.Bavail), 65 | }, 66 | { 67 | name: "files", 68 | t: ArgTypeUnsignedLong, 69 | raw: uintptr(stat.Files), 70 | }, 71 | { 72 | name: "ffree", 73 | t: ArgTypeUnsignedLong, 74 | raw: uintptr(stat.Ffree), 75 | }, 76 | { 77 | name: "namelen", 78 | t: ArgTypeUnsignedLong, 79 | raw: uintptr(stat.Namelen), 80 | }, 81 | { 82 | name: "frsize", 83 | t: ArgTypeUnsignedLong, 84 | raw: uintptr(stat.Frsize), 85 | }, 86 | { 87 | name: "flags", 88 | t: ArgTypeUnsignedLong, 89 | raw: uintptr(stat.Flags), 90 | }, 91 | }, 92 | }, nil 93 | } 94 | -------------------------------------------------------------------------------- /tracer/types_statx.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeStatX, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | if raw > 0 { 12 | // read the raw C struct from the process memory 13 | rawStat, err := readSize(pid, raw, unsafe.Sizeof(unix.Statx_t{})) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | var stat unix.Statx_t 19 | if err := decodeStruct(rawStat, &stat); err != nil { 20 | return err 21 | } 22 | 23 | arg.obj = convertStatx(&stat) 24 | arg.t = ArgTypeObject 25 | } else { 26 | arg.annotation = "NULL" 27 | arg.replace = true 28 | } 29 | return nil 30 | }) 31 | } 32 | 33 | func convertStatx(stat *unix.Statx_t) *Object { 34 | obj := Object{ 35 | Name: "statx", 36 | } 37 | 38 | obj.Properties = append(obj.Properties, Arg{ 39 | name: "mask", 40 | t: ArgTypeUnsignedInt, 41 | raw: uintptr(stat.Mask), 42 | }) 43 | 44 | obj.Properties = append(obj.Properties, Arg{ 45 | name: "blksize", 46 | t: ArgTypeUnsignedInt, 47 | raw: uintptr(stat.Blksize), 48 | }) 49 | 50 | obj.Properties = append(obj.Properties, Arg{ 51 | name: "attributes", 52 | t: ArgTypeUnsignedInt, 53 | raw: uintptr(stat.Attributes), 54 | }) 55 | 56 | obj.Properties = append(obj.Properties, Arg{ 57 | name: "nlink", 58 | t: ArgTypeUnsignedInt, 59 | raw: uintptr(stat.Nlink), 60 | }) 61 | 62 | obj.Properties = append(obj.Properties, Arg{ 63 | name: "uid", 64 | t: ArgTypeUnsignedInt, 65 | raw: uintptr(stat.Uid), 66 | }) 67 | 68 | obj.Properties = append(obj.Properties, Arg{ 69 | name: "gid", 70 | t: ArgTypeUnsignedInt, 71 | raw: uintptr(stat.Gid), 72 | }) 73 | 74 | obj.Properties = append(obj.Properties, Arg{ 75 | name: "mode", 76 | t: ArgTypeUnsignedInt, 77 | raw: uintptr(stat.Mode), 78 | annotation: permModeToString(uint32(stat.Mode)), 79 | replace: true, 80 | }) 81 | 82 | obj.Properties = append(obj.Properties, Arg{ 83 | name: "ino", 84 | t: ArgTypeUnsignedInt, 85 | raw: uintptr(stat.Ino), 86 | }) 87 | 88 | obj.Properties = append(obj.Properties, Arg{ 89 | name: "size", 90 | t: ArgTypeInt, 91 | raw: uintptr(stat.Size), 92 | }) 93 | 94 | obj.Properties = append(obj.Properties, Arg{ 95 | name: "blocks", 96 | t: ArgTypeInt, 97 | raw: uintptr(stat.Blocks), 98 | }) 99 | 100 | obj.Properties = append(obj.Properties, Arg{ 101 | name: "attributes_mask", 102 | t: ArgTypeUnsignedInt, 103 | raw: uintptr(stat.Attributes_mask), 104 | }) 105 | 106 | // - 107 | 108 | obj.Properties = append(obj.Properties, Arg{ 109 | name: "atime", 110 | t: ArgTypeObject, 111 | obj: convertStatXTimestamp(stat.Atime), 112 | }) 113 | obj.Properties = append(obj.Properties, Arg{ 114 | name: "btime", 115 | t: ArgTypeObject, 116 | obj: convertStatXTimestamp(stat.Btime), 117 | }) 118 | 119 | obj.Properties = append(obj.Properties, Arg{ 120 | name: "ctime", 121 | t: ArgTypeObject, 122 | obj: convertStatXTimestamp(stat.Ctime), 123 | }) 124 | 125 | obj.Properties = append(obj.Properties, Arg{ 126 | name: "mtime", 127 | t: ArgTypeObject, 128 | obj: convertStatXTimestamp(stat.Mtime), 129 | }) 130 | 131 | obj.Properties = append(obj.Properties, Arg{ 132 | name: "rdev_major", 133 | t: ArgTypeUnsignedInt, 134 | raw: uintptr(stat.Rdev_major), 135 | }) 136 | 137 | obj.Properties = append(obj.Properties, Arg{ 138 | name: "rdev_minor", 139 | t: ArgTypeUnsignedInt, 140 | raw: uintptr(stat.Rdev_minor), 141 | }) 142 | obj.Properties = append(obj.Properties, Arg{ 143 | name: "dev_major", 144 | t: ArgTypeUnsignedInt, 145 | raw: uintptr(stat.Dev_major), 146 | }) 147 | obj.Properties = append(obj.Properties, Arg{ 148 | name: "dev_minor", 149 | t: ArgTypeUnsignedInt, 150 | raw: uintptr(stat.Dev_minor), 151 | }) 152 | obj.Properties = append(obj.Properties, Arg{ 153 | name: "mnt_id", 154 | t: ArgTypeUnsignedInt, 155 | raw: uintptr(stat.Mnt_id), 156 | }) 157 | 158 | return &obj 159 | } 160 | 161 | func convertStatXTimestamp(t unix.StatxTimestamp) *Object { 162 | return &Object{ 163 | Name: "statx_timestamp", 164 | Properties: []Arg{ 165 | { 166 | name: "sec", 167 | t: ArgTypeUnsignedInt, 168 | raw: uintptr(t.Sec), 169 | }, 170 | { 171 | name: "nsec", 172 | t: ArgTypeUnsignedInt, 173 | raw: uintptr(t.Nsec), 174 | }, 175 | }, 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /tracer/types_string.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | ) 8 | 9 | /* 10 | NOTE: an array of strings in C (at least a char *argv[]) is an array of pointers to strings, terminated by a NULL pointer. 11 | */ 12 | 13 | func init() { 14 | registerTypeHandler(argTypeStringArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 15 | 16 | var items []Arg 17 | var offset uintptr 18 | size := unsafe.Sizeof(uintptr(0)) 19 | 20 | for { 21 | mem, err := readSize(pid, raw+offset, size) 22 | if err != nil { 23 | return err 24 | } 25 | address := uintptr(decodeUint(mem)) 26 | if address == 0 { 27 | break 28 | } 29 | str, err := readString(pid, address) 30 | if err != nil { 31 | return err 32 | } 33 | items = append(items, Arg{ 34 | t: ArgTypeData, 35 | raw: address, 36 | data: []byte(str), 37 | }) 38 | offset += size 39 | } 40 | arg.t = ArgTypeArray 41 | arg.array = items 42 | return nil 43 | }) 44 | registerTypeHandler(argTypeString, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 45 | str, err := readString(pid, raw) 46 | if err != nil { 47 | return err 48 | } 49 | arg.data = []byte(str) 50 | arg.t = ArgTypeData 51 | return nil 52 | }) 53 | } 54 | 55 | func readString(pid int, addr uintptr) (string, error) { 56 | var output string 57 | if addr == 0 { 58 | return output, nil 59 | } 60 | data := make([]byte, 1) 61 | for { 62 | if _, err := syscall.PtracePeekData(pid, addr, data); err != nil { 63 | return "", fmt.Errorf("read of 0x%x failed: %w", addr, err) 64 | } 65 | if data[0] == 0 { 66 | break 67 | } 68 | output += string(data) 69 | addr++ 70 | } 71 | return output, nil 72 | } 73 | -------------------------------------------------------------------------------- /tracer/types_sysinfo.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeSysinfo, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | rawVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Sysinfo_t{})) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | var sysinfo unix.Sysinfo_t 19 | if err := decodeStruct(rawVal, &sysinfo); err != nil { 20 | return err 21 | } 22 | 23 | arg.obj, err = convertSysinfo(&sysinfo, pid) 24 | if err != nil { 25 | return err 26 | } 27 | arg.t = ArgTypeObject 28 | } else { 29 | arg.annotation = "NULL" 30 | arg.replace = true 31 | } 32 | return nil 33 | }) 34 | } 35 | 36 | func convertLoadsToArray(loads [3]uint64) []Arg { 37 | var loadsArray []Arg 38 | for _, load := range loads { 39 | loadsArray = append(loadsArray, Arg{ 40 | name: "load", 41 | t: ArgTypeUnsignedLong, 42 | raw: uintptr(load), 43 | }) 44 | } 45 | return loadsArray 46 | } 47 | 48 | func convertSysinfo(sysinfo *unix.Sysinfo_t, _ int) (*Object, error) { 49 | return &Object{ 50 | Name: "sysinfo", 51 | Properties: []Arg{ 52 | { 53 | name: "uptime", 54 | t: ArgTypeUnsignedLong, 55 | raw: uintptr(sysinfo.Uptime), 56 | }, 57 | { 58 | name: "loads", 59 | t: ArgTypeArray, 60 | array: convertLoadsToArray(sysinfo.Loads), 61 | }, 62 | { 63 | name: "totalram", 64 | t: ArgTypeUnsignedLong, 65 | raw: uintptr(sysinfo.Totalram), 66 | }, 67 | { 68 | name: "freeram", 69 | t: ArgTypeUnsignedLong, 70 | raw: uintptr(sysinfo.Freeram), 71 | }, 72 | { 73 | name: "sharedram", 74 | t: ArgTypeUnsignedLong, 75 | raw: uintptr(sysinfo.Sharedram), 76 | }, 77 | { 78 | name: "bufferram", 79 | t: ArgTypeUnsignedLong, 80 | raw: uintptr(sysinfo.Bufferram), 81 | }, 82 | { 83 | name: "totalswap", 84 | t: ArgTypeUnsignedLong, 85 | raw: uintptr(sysinfo.Totalswap), 86 | }, 87 | { 88 | name: "freeswap", 89 | t: ArgTypeUnsignedLong, 90 | raw: uintptr(sysinfo.Freeswap), 91 | }, 92 | { 93 | name: "procs", 94 | t: ArgTypeUnsignedInt, 95 | raw: uintptr(sysinfo.Procs), 96 | }, 97 | { 98 | name: "totalhigh", 99 | t: ArgTypeUnsignedLong, 100 | raw: uintptr(sysinfo.Totalhigh), 101 | }, 102 | { 103 | name: "freehigh", 104 | t: ArgTypeUnsignedLong, 105 | raw: uintptr(sysinfo.Freehigh), 106 | }, 107 | { 108 | name: "mem_unit", 109 | t: ArgTypeUnsignedInt, 110 | raw: uintptr(sysinfo.Unit), 111 | }, 112 | }, 113 | }, nil 114 | } 115 | -------------------------------------------------------------------------------- /tracer/types_timespec.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "unsafe" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | func init() { 12 | registerTypeHandler(argTypeTimespec, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 13 | 14 | if raw > 0 { 15 | // read the raw C struct from the process memory 16 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Timespec{})) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | var timeVal unix.Timespec 22 | if err := decodeStruct(rawTimeVal, &timeVal); err != nil { 23 | return err 24 | } 25 | 26 | arg.obj = convertTimeSpec(&timeVal) 27 | arg.t = ArgTypeObject 28 | } else { 29 | arg.annotation = "NULL" 30 | arg.replace = true 31 | } 32 | return nil 33 | }) 34 | registerTypeHandler(argTypeTimespecArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 35 | 36 | if raw > 0 { 37 | 38 | var size int 39 | 40 | switch metadata.LenSource { 41 | case LenSourceFixed: 42 | size = metadata.FixedCount 43 | default: 44 | return fmt.Errorf("unsupported count_from: %d", metadata.LenSource) 45 | } 46 | 47 | rawVals, err := readSize(pid, raw, uintptr(size)*unsafe.Sizeof(unix.Timespec{})) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | vals := make([]unix.Timespec, size) 53 | if err := decodeAnonymous(reflect.ValueOf(&vals).Elem(), rawVals); err != nil { 54 | return err 55 | } 56 | 57 | for _, val := range vals { 58 | arg.array = append(arg.array, Arg{ 59 | t: ArgTypeObject, 60 | obj: convertTimeSpec(&val), 61 | }) 62 | } 63 | } 64 | 65 | arg.t = ArgTypeArray 66 | return nil 67 | }) 68 | } 69 | 70 | func convertTimeSpec(u *unix.Timespec) *Object { 71 | if u == nil { 72 | return nil 73 | } 74 | return &Object{ 75 | Name: "timespec", 76 | Properties: []Arg{ 77 | { 78 | name: "sec", 79 | t: ArgTypeUnsignedLong, 80 | raw: uintptr(u.Sec), 81 | }, 82 | { 83 | name: "usec", 84 | t: ArgTypeUnsignedLong, 85 | raw: uintptr(u.Nsec), 86 | }, 87 | }, 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /tracer/types_timeval.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "unsafe" 7 | 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | func init() { 12 | registerTypeHandler(argTypeTimeval, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 13 | 14 | if raw > 0 { 15 | // read the raw C struct from the process memory 16 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Timeval{})) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | var timeVal unix.Timeval 22 | if err := decodeStruct(rawTimeVal, &timeVal); err != nil { 23 | return err 24 | } 25 | 26 | arg.obj = convertTimeVal(&timeVal) 27 | arg.t = ArgTypeObject 28 | } else { 29 | arg.annotation = "NULL" 30 | arg.replace = true 31 | } 32 | return nil 33 | }) 34 | registerTypeHandler(argTypeTimevalArray, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 35 | 36 | if raw > 0 { 37 | 38 | var size int 39 | 40 | switch metadata.LenSource { 41 | case LenSourceFixed: 42 | size = metadata.FixedCount 43 | default: 44 | return fmt.Errorf("unsupported count_from: %d", metadata.LenSource) 45 | } 46 | 47 | rawVals, err := readSize(pid, raw, uintptr(size)*unsafe.Sizeof(unix.Timeval{})) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | vals := make([]unix.Timeval, size) 53 | if err := decodeAnonymous(reflect.ValueOf(&vals).Elem(), rawVals); err != nil { 54 | return err 55 | } 56 | 57 | for _, val := range vals { 58 | arg.array = append(arg.array, Arg{ 59 | t: ArgTypeObject, 60 | obj: convertTimeVal(&val), 61 | }) 62 | } 63 | } 64 | 65 | arg.t = ArgTypeArray 66 | return nil 67 | }) 68 | } 69 | 70 | func convertTimeVal(u *unix.Timeval) *Object { 71 | return &Object{ 72 | Name: "timeval", 73 | Properties: []Arg{ 74 | { 75 | name: "sec", 76 | t: ArgTypeUnsignedLong, 77 | raw: uintptr(u.Sec), 78 | }, 79 | { 80 | name: "usec", 81 | t: ArgTypeUnsignedLong, 82 | raw: uintptr(u.Usec), 83 | }, 84 | }, 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tracer/types_timex.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeTimex, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | // read the raw C struct from the process memory 14 | mem, err := readSize(pid, raw, unsafe.Sizeof(unix.Timex{})) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | var tx unix.Timex 20 | if err := decodeStruct(mem, &tx); err != nil { 21 | return err 22 | } 23 | 24 | arg.obj = convertTimex(&tx) 25 | arg.t = ArgTypeObject 26 | } else { 27 | arg.annotation = "NULL" 28 | arg.replace = true 29 | } 30 | return nil 31 | }) 32 | } 33 | 34 | func convertTimex(tx *unix.Timex) *Object { 35 | return &Object{ 36 | Name: "timezone", 37 | Properties: []Arg{ 38 | { 39 | name: "modes", 40 | t: ArgTypeInt, 41 | raw: uintptr(tx.Modes), 42 | }, 43 | { 44 | name: "offset", 45 | t: ArgTypeInt, 46 | raw: uintptr(tx.Offset), 47 | }, 48 | { 49 | name: "freq", 50 | t: ArgTypeInt, 51 | raw: uintptr(tx.Freq), 52 | }, 53 | { 54 | name: "maxerror", 55 | t: ArgTypeInt, 56 | raw: uintptr(tx.Maxerror), 57 | }, 58 | { 59 | name: "esterror", 60 | t: ArgTypeInt, 61 | raw: uintptr(tx.Esterror), 62 | }, 63 | { 64 | name: "status", 65 | t: ArgTypeInt, 66 | raw: uintptr(tx.Status), 67 | }, 68 | { 69 | name: "constant", 70 | t: ArgTypeInt, 71 | raw: uintptr(tx.Constant), 72 | }, 73 | { 74 | name: "precision", 75 | t: ArgTypeInt, 76 | raw: uintptr(tx.Precision), 77 | }, 78 | { 79 | name: "tolerance", 80 | t: ArgTypeInt, 81 | raw: uintptr(tx.Tolerance), 82 | }, 83 | { 84 | name: "time", 85 | t: ArgTypeObject, 86 | obj: convertTimeVal(&tx.Time), 87 | }, 88 | { 89 | name: "tick", 90 | t: ArgTypeInt, 91 | raw: uintptr(tx.Tick), 92 | }, 93 | { 94 | name: "ppsfreq", 95 | t: ArgTypeInt, 96 | raw: uintptr(tx.Ppsfreq), 97 | }, 98 | { 99 | name: "jitter", 100 | t: ArgTypeInt, 101 | raw: uintptr(tx.Jitter), 102 | }, 103 | { 104 | name: "shift", 105 | t: ArgTypeInt, 106 | raw: uintptr(tx.Shift), 107 | }, 108 | { 109 | name: "stabil", 110 | t: ArgTypeInt, 111 | raw: uintptr(tx.Stabil), 112 | }, 113 | { 114 | name: "jitcnt", 115 | t: ArgTypeInt, 116 | raw: uintptr(tx.Jitcnt), 117 | }, 118 | { 119 | name: "calcnt", 120 | t: ArgTypeInt, 121 | raw: uintptr(tx.Calcnt), 122 | }, 123 | { 124 | name: "errcnt", 125 | t: ArgTypeInt, 126 | raw: uintptr(tx.Errcnt), 127 | }, 128 | { 129 | name: "stbcnt", 130 | t: ArgTypeInt, 131 | raw: uintptr(tx.Stbcnt), 132 | }, 133 | { 134 | name: "tai", 135 | t: ArgTypeInt, 136 | raw: uintptr(tx.Tai), 137 | }, 138 | }, 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /tracer/types_timezone.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type timezone struct { 8 | Minuteswest int32 9 | Dsttime int32 10 | } 11 | 12 | func init() { 13 | registerTypeHandler(argTypeTimezone, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 14 | 15 | if raw > 0 { 16 | // read the raw C struct from the process memory 17 | mem, err := readSize(pid, raw, unsafe.Sizeof(timezone{})) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | var tz timezone 23 | if err := decodeStruct(mem, &tz); err != nil { 24 | return err 25 | } 26 | 27 | arg.obj = convertTimezone(&tz) 28 | arg.t = ArgTypeObject 29 | } else { 30 | arg.annotation = "NULL" 31 | arg.replace = true 32 | } 33 | return nil 34 | }) 35 | } 36 | 37 | func convertTimezone(tz *timezone) *Object { 38 | return &Object{ 39 | Name: "timezone", 40 | Properties: []Arg{ 41 | { 42 | name: "minuteswest", 43 | t: ArgTypeInt, 44 | raw: uintptr(tz.Minuteswest), 45 | }, 46 | { 47 | name: "dsttime", 48 | t: ArgTypeInt, 49 | raw: uintptr(tz.Dsttime), 50 | }, 51 | }, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tracer/types_tms.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeTms, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | rawVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Tms{})) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | var times unix.Tms 19 | if err := decodeStruct(rawVal, ×); err != nil { 20 | return err 21 | } 22 | 23 | arg.obj = convertTimes(×) 24 | arg.t = ArgTypeObject 25 | } else { 26 | arg.annotation = "NULL" 27 | arg.replace = true 28 | } 29 | return nil 30 | }) 31 | } 32 | 33 | func convertTimes(times *unix.Tms) *Object { 34 | return &Object{ 35 | Name: "times", 36 | Properties: []Arg{ 37 | { 38 | name: "utime", 39 | t: ArgTypeUnsignedLong, 40 | raw: uintptr(times.Utime), 41 | }, 42 | { 43 | name: "stime", 44 | t: ArgTypeUnsignedLong, 45 | raw: uintptr(times.Stime), 46 | }, 47 | { 48 | name: "cutime", 49 | t: ArgTypeUnsignedLong, 50 | raw: uintptr(times.Cutime), 51 | }, 52 | { 53 | name: "cstime", 54 | t: ArgTypeUnsignedLong, 55 | raw: uintptr(times.Cstime), 56 | }, 57 | }, 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tracer/types_uname.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "syscall" 5 | "unsafe" 6 | ) 7 | 8 | func init() { 9 | registerTypeHandler(argTypeUname, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 10 | if raw > 0 { 11 | data, err := readSize(pid, raw, unsafe.Sizeof(syscall.Utsname{})) 12 | if err != nil { 13 | return err 14 | } 15 | 16 | var uname syscall.Utsname 17 | if err := decodeStruct(data, &uname); err != nil { 18 | return err 19 | } 20 | 21 | arg.obj = convertUname(&uname) 22 | arg.t = ArgTypeObject 23 | } else { 24 | arg.t = ArgTypeAddress 25 | arg.annotation = "NULL" 26 | arg.replace = true 27 | } 28 | return nil 29 | }) 30 | } 31 | 32 | func getUtsField(f [65]int8) []byte { 33 | var str []byte 34 | for i := 0; i < len(f); i++ { 35 | if f[i] == 0 { 36 | break 37 | } 38 | str = append(str, byte(f[i])) 39 | } 40 | return str 41 | } 42 | 43 | func convertUname(uname *syscall.Utsname) *Object { 44 | return &Object{ 45 | Name: "uname", 46 | Properties: []Arg{ 47 | { 48 | name: "sysname", 49 | t: ArgTypeData, 50 | data: getUtsField(uname.Sysname), 51 | }, 52 | { 53 | name: "nodename", 54 | t: ArgTypeData, 55 | data: getUtsField(uname.Nodename), 56 | }, 57 | { 58 | name: "release", 59 | t: ArgTypeData, 60 | data: getUtsField(uname.Release), 61 | }, 62 | { 63 | name: "version", 64 | t: ArgTypeData, 65 | data: getUtsField(uname.Version), 66 | }, 67 | { 68 | name: "machine", 69 | t: ArgTypeData, 70 | data: getUtsField(uname.Machine), 71 | }, 72 | { 73 | name: "domainname", 74 | t: ArgTypeData, 75 | data: getUtsField(uname.Domainname), 76 | }, 77 | }, 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /tracer/types_userdesc.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | ) 6 | 7 | type ldt struct { 8 | EntryNumber uint32 9 | BaseAddr uint32 10 | Limit uint32 11 | } 12 | 13 | func init() { 14 | registerTypeHandler(argTypeUserDesc, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 15 | 16 | if raw > 0 { 17 | rawVal, err := readSize(pid, raw, unsafe.Sizeof(ldt{})) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | var table ldt 23 | if err := decodeStruct(rawVal, &table); err != nil { 24 | return err 25 | } 26 | 27 | arg.obj = convertLDT(table) 28 | if err != nil { 29 | return err 30 | } 31 | arg.t = ArgTypeObject 32 | } else { 33 | arg.annotation = "NULL" 34 | arg.replace = true 35 | } 36 | return nil 37 | }) 38 | } 39 | 40 | func convertLDT(table ldt) *Object { 41 | return &Object{ 42 | Name: "ldt", 43 | Properties: []Arg{ 44 | { 45 | name: "entry_number", 46 | t: ArgTypeUnsignedLong, 47 | raw: uintptr(table.EntryNumber), 48 | }, 49 | { 50 | name: "base_addr", 51 | t: ArgTypeUnsignedLong, 52 | raw: uintptr(table.BaseAddr), 53 | }, 54 | { 55 | name: "limit", 56 | t: ArgTypeUnsignedLong, 57 | raw: uintptr(table.Limit), 58 | }, 59 | }, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tracer/types_ustat.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeUstat, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | rawVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Ustat_t{})) 14 | if err != nil { 15 | return err 16 | } 17 | 18 | var stat unix.Ustat_t 19 | if err := decodeStruct(rawVal, &stat); err != nil { 20 | return err 21 | } 22 | 23 | arg.obj, err = convertUstat(&stat, pid) 24 | if err != nil { 25 | return err 26 | } 27 | arg.t = ArgTypeObject 28 | } else { 29 | arg.annotation = "NULL" 30 | arg.replace = true 31 | } 32 | return nil 33 | }) 34 | } 35 | 36 | func convertUstat(stat *unix.Ustat_t, _ int) (*Object, error) { 37 | 38 | var fname []byte 39 | for _, b := range stat.Fname { 40 | if b == 0 { 41 | break 42 | } 43 | fname = append(fname, byte(b)) 44 | } 45 | 46 | var fpack []byte 47 | for _, b := range stat.Fpack { 48 | if b == 0 { 49 | break 50 | } 51 | fpack = append(fpack, byte(b)) 52 | } 53 | 54 | return &Object{ 55 | Name: "ustat", 56 | Properties: []Arg{ 57 | { 58 | name: "tfree", 59 | t: ArgTypeUnsignedLong, 60 | raw: uintptr(stat.Tfree), 61 | }, 62 | { 63 | name: "tinode", 64 | t: ArgTypeUnsignedLong, 65 | raw: uintptr(stat.Tinode), 66 | }, 67 | { 68 | name: "fname", 69 | t: ArgTypeData, 70 | data: fname, 71 | }, 72 | { 73 | name: "fpack", 74 | t: ArgTypeData, 75 | data: fpack, 76 | }, 77 | }, 78 | }, nil 79 | } 80 | -------------------------------------------------------------------------------- /tracer/types_utimbuf.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeUtimbuf, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | // read the raw C struct from the process memory 14 | rawTimeVal, err := readSize(pid, raw, unsafe.Sizeof(unix.Utimbuf{})) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | var timeVal unix.Utimbuf 20 | if err := decodeStruct(rawTimeVal, &timeVal); err != nil { 21 | return err 22 | } 23 | 24 | arg.obj = convertTimeBuf(&timeVal) 25 | arg.t = ArgTypeObject 26 | } else { 27 | arg.annotation = "NULL" 28 | arg.replace = true 29 | } 30 | return nil 31 | }) 32 | } 33 | 34 | func convertTimeBuf(u *unix.Utimbuf) *Object { 35 | return &Object{ 36 | Name: "timbuf", 37 | Properties: []Arg{ 38 | { 39 | name: "actime", 40 | t: ArgTypeUnsignedLong, 41 | raw: uintptr(u.Actime), 42 | }, 43 | { 44 | name: "modtime", 45 | t: ArgTypeUnsignedLong, 46 | raw: uintptr(u.Modtime), 47 | }, 48 | }, 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tracer/types_waitstatus.go: -------------------------------------------------------------------------------- 1 | package tracer 2 | 3 | import ( 4 | "unsafe" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func init() { 10 | registerTypeHandler(argTypeWaitStatus, func(arg *Arg, metadata ArgMetadata, raw, next, prev, ret uintptr, pid int) error { 11 | 12 | if raw > 0 { 13 | // read the raw C struct from the process memory 14 | bytes, err := readSize(pid, raw, unsafe.Sizeof(unix.WaitStatus(0))) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | status := uintptr(decodeUint(bytes)) 20 | 21 | arg.raw = status 22 | arg.t = ArgTypeUnsignedInt 23 | } else { 24 | arg.t = ArgTypeAddress 25 | arg.annotation = "NULL" 26 | arg.replace = true 27 | } 28 | return nil 29 | }) 30 | } 31 | -------------------------------------------------------------------------------- /ux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liamg/grace/a28e5c24b4666a6c6674c152a5435fbde628f6b2/ux.png --------------------------------------------------------------------------------