gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/g3doc/user_guide/debugging.md (about) 1 # Debugging 2 3 [TOC] 4 5 To enable debug and system call logging, add the `runtimeArgs` below to your 6 [Docker](../quick_start/docker/) configuration (`/etc/docker/daemon.json`): 7 8 ```json 9 { 10 "runtimes": { 11 "runsc": { 12 "path": "/usr/local/bin/runsc", 13 "runtimeArgs": [ 14 "--debug-log=/tmp/runsc/", 15 "--debug", 16 "--strace" 17 ] 18 } 19 } 20 } 21 ``` 22 23 > Note: the last `/` in `--debug-log` is needed to interpret it as a directory. 24 > Then each `runsc` command executed will create a separate log file. Otherwise, 25 > log messages from all commands will be appended to the same file. 26 27 You may also want to pass `--log-packets` to troubleshoot network problems. Then 28 restart the Docker daemon: 29 30 ```bash 31 sudo systemctl restart docker 32 ``` 33 34 Run your container again, and inspect the files under `/tmp/runsc`. The log file 35 ending with `.boot` will contain the strace logs from your application, which 36 can be useful for identifying missing or broken system calls in gVisor. If you 37 are having problems starting the container, the log file ending with `.create` 38 may have the reason for the failure. 39 40 ## Stack traces 41 42 The command `runsc debug --stacks` collects stack traces while the sandbox is 43 running which can be useful to troubleshoot issues or just to learn more about 44 gVisor. It connects to the sandbox process, collects a stack dump, and writes it 45 to the console. For example: 46 47 ```bash 48 docker run --runtime=runsc --rm -d alpine sh -c "while true; do echo running; sleep 1; done" 49 63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b 50 51 sudo runsc --root /var/run/docker/runtime-runsc/moby debug --stacks 63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b 52 ``` 53 54 > Note: `--root` variable is provided by docker and is normally set to 55 > `/var/run/docker/runtime-[runtime-name]/moby`. If in doubt, `--root` is logged 56 > to `runsc` logs. 57 58 ## Debugger 59 60 You can debug gVisor like any other Golang program. If you're running with 61 Docker, you'll need to find the sandbox PID and attach the debugger as root. 62 Here is an example: 63 64 Install a runsc with debug symbols (you can also use the 65 [nightly release](../install/#nightly)): 66 67 ```bash 68 make dev BAZEL_OPTIONS="-c dbg --define gotags=debug" 69 ``` 70 71 Start the container you want to debug using the runsc runtime with debug 72 options: 73 74 ```bash 75 docker run --runtime=$(git branch --show-current)-d --rm --name=test -p 8080:80 -d nginx 76 ``` 77 78 Find the PID and attach your favorite debugger: 79 80 ```bash 81 sudo dlv attach $(docker inspect test | grep Pid | head -n 1 | grep -oe "[0-9]*") 82 ``` 83 84 Set a breakpoint for accept: 85 86 ```bash 87 break gvisor.dev/gvisor/pkg/sentry/socket/netstack.(*sock).Accept 88 continue 89 ``` 90 91 In a different window connect to nginx to trigger the breakpoint: 92 93 ```bash 94 curl http://localhost:8080/ 95 ``` 96 97 It's also easy to attach a debugger to one of the predefined syscall tests when 98 you're working on specific gVisor features. With the `delay-for-debugger` flag 99 you can pause the test runner before execution so that you can attach the 100 sandbox process to a debugger. Here is an example: 101 102 ```bash 103 make test BAZEL_OPTIONS="-c dbg --define gotags=debug" \ 104 OPTIONS="--test_arg=--delay-for-debugger=5m --test_output=streamed" \ 105 TARGETS=//test/syscalls:mount_test_runsc_systrap 106 ``` 107 108 The `delay-for-debugger=5m` flag means the test runner will pause for 5 minutes 109 before running the test. To attach to the sandbox process, you can run the 110 following in a separate window. 111 112 ```bash 113 dlv attach $(ps aux | grep -m 1 -e 'runsc-sandbox' | awk '{print $2}') 114 ``` 115 116 Once you've attached to the process and set a breakpoint, you can signal the 117 test to start by running the following in another separate window. 118 119 ```bash 120 kill -SIGUSR1 $(ps aux | grep -m 1 -e 'bash.*test/syscalls' | awk '{print $2}') 121 ``` 122 123 ## Profiling 124 125 `runsc` integrates with Go profiling tools and gives you easy commands to 126 profile CPU and heap usage. First you need to enable `--profile` in the command 127 line options before starting the container: 128 129 ```json 130 { 131 "runtimes": { 132 "runsc-prof": { 133 "path": "/usr/local/bin/runsc", 134 "runtimeArgs": [ 135 "--profile" 136 ] 137 } 138 } 139 } 140 ``` 141 142 > Note: Enabling profiling loosens the seccomp protection added to the sandbox, 143 > and should not be run in production under normal circumstances. 144 145 Then restart docker to refresh the runtime options. While the container is 146 running, execute `runsc debug` to collect profile information and save to a 147 file. Here are the options available: 148 149 * **--profile-heap:** Generates heap profile to the speficied file. 150 * **--profile-cpu:** Enables CPU profiler, waits for `--duration` seconds and 151 generates CPU profile to the speficied file. 152 153 For example: 154 155 ```bash 156 docker run --runtime=runsc-prof --rm -d alpine sh -c "while true; do echo running; sleep 1; done" 157 63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b 158 159 sudo runsc --root /var/run/docker/runtime-runsc-prof/moby debug --profile-heap=/tmp/heap.prof 63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b 160 sudo runsc --root /var/run/docker/runtime-runsc-prof/moby debug --profile-cpu=/tmp/cpu.prof --duration=30s 63254c6ab3a6989623fa1fb53616951eed31ac605a2637bb9ddba5d8d404b35b 161 ``` 162 163 The resulting files can be opened using `go tool pprof` or [pprof][]. The 164 examples below create image file (`.svg`) with the heap profile and writes the 165 top functions using CPU to the console: 166 167 ```bash 168 go tool pprof -svg /usr/local/bin/runsc /tmp/heap.prof 169 go tool pprof -top /usr/local/bin/runsc /tmp/cpu.prof 170 ``` 171 172 [pprof]: https://github.com/google/pprof/blob/master/doc/README.md 173 174 ### Docker Proxy 175 176 When forwarding a port to the container, Docker will likely route traffic 177 through the [docker-proxy][]. This proxy may make profiling noisy, so it can be 178 helpful to bypass it. Do so by sending traffic directly to the container IP and 179 port. e.g., if the `docker0` IP is `192.168.9.1`, the container IP is likely a 180 subsequent IP, such as `192.168.9.2`. 181 182 [docker-proxy]: https://windsock.io/the-docker-proxy/