{"id":43303835,"url":"https://github.com/stevenjohnstone/go-bpf-gen","last_synced_at":"2026-02-01T20:11:21.223Z","repository":{"id":57626888,"uuid":"402510911","full_name":"stevenjohnstone/go-bpf-gen","owner":"stevenjohnstone","description":"Generate bpftrace scripts for use with golang programs. Works around quirks in the golang runtime.","archived":false,"fork":false,"pushed_at":"2025-06-21T12:00:01.000Z","size":82,"stargazers_count":59,"open_issues_count":0,"forks_count":9,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-21T13:18:36.012Z","etag":null,"topics":["bpf","bpftrace","debugging","ebpf","golang","observability"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stevenjohnstone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-02T17:41:55.000Z","updated_at":"2025-06-21T12:00:04.000Z","dependencies_parsed_at":"2023-01-22T20:00:46.863Z","dependency_job_id":null,"html_url":"https://github.com/stevenjohnstone/go-bpf-gen","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/stevenjohnstone/go-bpf-gen","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenjohnstone%2Fgo-bpf-gen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenjohnstone%2Fgo-bpf-gen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenjohnstone%2Fgo-bpf-gen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenjohnstone%2Fgo-bpf-gen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stevenjohnstone","download_url":"https://codeload.github.com/stevenjohnstone/go-bpf-gen/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stevenjohnstone%2Fgo-bpf-gen/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28988634,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T18:17:03.387Z","status":"ssl_error","status_checked_at":"2026-02-01T18:16:57.287Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bpf","bpftrace","debugging","ebpf","golang","observability"],"created_at":"2026-02-01T20:11:18.418Z","updated_at":"2026-02-01T20:11:21.216Z","avatar_url":"https://github.com/stevenjohnstone.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# About\n\nGenerate bpftrace programs suitable for tracing a golang program on x86-64.\n\n# Why?\n\nUsing bpftrace with golang isn't as straightforward as for C/C++ compiled code. See\nthis [blog](https://www.brendangregg.com/blog/2017-01-31/golang-bcc-bpf-function-tracing.html)\npost by Brendan Gregg.\n\n\n## Problem 1: Stacks can grow and be copied\n\nuretprobes are implemented by hijacking return addresses on the stack. Golang can grow\nstacks and in doing so move the stack contents. This can result in golang panics when being\ntraced. To get around this, the template generator looks for the addresses of RET instructions\nin the targeted function and creates uprobes for those.\n\n## Problem 2: Goroutines don't map 1-1 with system threads\n\nIt's not a problem for golang but it's a problem when trying to do per-thread statistics\nwith bpftrace. A goroutine may move around system threads so we need a way to map goroutine\nIDs to thread IDs.\n\n# Solution\n\nThis program can generate bpftrace programs from templates, filling in details like the\nlocation of RET instructions, offsets of goroutine ID values in structures etc.\n\n# Bundled Scripts\n\n## goroutine.bt\nThe script generated by\n\n```\ngo-bpf-gen templates/goroutine.bt \u003ctarget binary\u003e\n```\nprints a message whenever a goroutine is spawned.\n\n## httpsnoop.bt\nThe script generated by\n\n```\ngo-bpf-gen templates/httpsnoop.bt \u003ctarget binary\u003e\n```\ntracks outgoing HTTP requests.\n\n## latency.bt\n\nThe script generated by\n```\ngo-bpf-gen templates/latency.bt \u003ctarget binary\u003e symbol='\u003csymbol name\u003e' [symbol='\u003csymbol name\u003e']\n\n```\n\nwill measure the time spent in functions specified in the `symbol` parameters.\n\n## recover.bt\n\nThe script generated by\n```\ngo-bpf-gen templates/recover.bt \u003ctarget binary\u003e\n\n```\nwill record stack traces from calls to `recover()` after a panic.\n\n## shortread.bt\nThe script generated by\n```\ngo-bpf-gen templates/shortread.bt \u003ctarget binary\u003e\n```\nwill record stack traces from calls to [`func(f *os.File) Read([]byte) (int, error)`](https://pkg.go.dev/os#File.Read) which read fewer bytes than the length of the input buffer. This is a [common\nprogramming mistake](https://github.com/golang/go/issues/48182) in golang.\n\n## skeleton.bt\nThe script generated by\n```\ngo-bpf-gen templates/shortread.bt \u003ctarget binary\u003e symbol='\u003csymbol name\u003e' [symbol='\u003csymbol name\u003e']\n```\nhas empty `uprobe` functions which trace the entry and exit points of\nthe functions with specified symbols.\n\n## tcpremote.bt\nThe script generated by\n```\ngo-bpf-gen templates/tcpremote.bt \u003ctarget binary\u003e\n\n```\nwill output address and port for remote servers to which the program makes connections.\n\n## tlssecrets.bt\nThe script generated by\n```\ngo-bpf-gen templates/tlssecrets.bt \u003ctarget binary\u003e\n\n```\nwill output secrets which can be used with wireshark to decode\nnetwork traces of TLS connections made to/from the program.\n\nHere's how to use this to inspect TLS traffic from dockerd\n\n1. Generate ``tls.bt`` with ```go-bpf-gen templates/tlssecrets.bt /usr/bin/dockerd \u003e tls.bt```\n2. run bpftrace: ```sudo bpftrace tls.bt | sed '1d;s/\\\\x//g' \u003e secrets.txt```\n3. start a capture of traffic e.g. ```sudo tcpdump -nnSX port 443 -w https.pcap```\n4. do something with docker e.g. ```docker pull alpine```\n5. stop the tcpdump capture and bpftrace run.\n6. inject TLS secrets with ```editcap --inject-secrets tls,secrets.txt https.pcap decrypted.pcap```\n7. open ```decrypted.pcap``` with wireshark and notice there's plaintext of TLS traffic e.g.\n\n![image](https://user-images.githubusercontent.com/6546704/197301643-68fb1b0a-acf4-4b8a-b190-77a14a0bcac4.png)\n\nSimilar functionality is provided by [this](https://github.com/stevenjohnstone/reversing-tools/blob/main/golang/tls_secrets.py) [Frida](https://frida.re) script.\n\n## random.bt\nSnoop on reads from cryptographic random number generators with a script from\n```\ngo-bpf-get templates/random.bt \u003ctarget binary\u003e\n```\nThis will output the bytes read from the random number generator. Clearly, this may compromise the security of any cryptography the program happens to be\nusing.\n\n\n\n\n\n\n# Build \u0026 Install\n\n```\ngo install github.com/stevenjohnstone/go-bpf-gen@latest\n\n```\n\n# Usage\n\n\n```\ngo-bpf-gen \u003ctemplate\u003e \u003cexecutable path\u003e [key=value]\n\n```\n\nExample:\n\nLet's find who dockerd makes connections to when we do a `docker pull`.\nWe generate a bpftrace script to instrument the `dockerd` executable (it's written in go) using the builtin\n`templates/httpsnoop.bt` template. Execute\n\n```\ngo-bpf-gen templates/httpsnoop.bt $(which dockerd) \u003e dockerd.bt\n```\nThis gives a bpftrace script tailored to the target executable\n\n```bpftrace\nstruct url {\n  uint8_t *scheme;\n  int schemelen;\n  uint8_t *opaque;\n  int opaquelen;\n  uint64_t pad;\n  uint8_t *host;\n  int hostlen;\n  uint8_t *path;\n  int pathlen;\n};\n\nstruct request {\n  uint8_t pad[16];\n  struct url *url;\n};\n\nstruct response {\n  uint8_t *statusstr;\n  uint8_t *statusstrlen;\n  int statuscode;\n};\n\n\nuprobe:/usr/bin/dockerd:runtime.execute {\n\t// map thread id to goroutine id\n\t@gids[tid] = sarg0\n}\n\ntracepoint:sched:sched_process_exit {\n  delete(@rscheme[@gids[tid]]);\n  delete(@rhost[@gids[tid]]);\n  delete(@rpath[@gids[tid]]);\n  delete(@gids[tid]);\n}\n\n\nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" {\n  $url = ((struct request *)sarg1)-\u003eurl;\n  $scheme = str($url-\u003escheme, $url-\u003eschemelen);\n  $host = str($url-\u003ehost, $url-\u003ehostlen);\n  $path = str($url-\u003epath, $url-\u003epathlen);\n\n  @rscheme[@gids[tid]] = $scheme;\n  @rhost[@gids[tid]] = $host;\n  @rpath[@gids[tid]] = $path;\n}\n\n\nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 364, \nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 1225, \nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 1394, \nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 2822, \nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 2965, \nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 3456, \nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 4134, \nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 4387, \nuprobe:/usr/bin/dockerd:\"net/http.(*Client).do\" + 4511 {\n  $resp = (struct response *)reg(\"ax\"); // XXXSJJ: rax contains pointer to the response\n  if ($resp == 0) {\n    printf(\"error %s://%s%s\\n\", @rscheme[@gids[tid]], @rhost[@gids[tid]], @rpath[@gids[tid]]);\n  } else {\n    printf(\"%d: %s://%s%s\\n\", $resp-\u003estatuscode, @rscheme[@gids[tid]], @rhost[@gids[tid]], @rpath[@gids[tid]]);\n  }\n  print(ustack());\n}\n\n```\n\nWhen I execute `docker pull alpine` on my system, the above script outputs\n\n```\nAttaching 12 probes...\n401: https://registry-1.docker.io/v2/\n\n        net/http.(*Client).do+1225\n        local.github.com/docker/docker/distribution.NewV2Repository+627\n        github.com/docker/docker/distribution.(*v2Puller).Pull+270\n        github.com/docker/docker/distribution.Pull+1525\n        github.com/docker/docker/daemon/images.(*ImageService).pullImageWithReference+1345\n        github.com/docker/docker/daemon/images.(*ImageService).PullImage+350\n        local.github.com/docker/docker/api/server/router/image.(*imageRouter).postImagesCreate+1666\n        github.com/docker/docker/api/server/router/image.(*imageRouter).postImagesCreate-fm+107\n        github.com/docker/docker/api/server/middleware.ExperimentalMiddleware.WrapHandler.func1+375\n        local.github.com/docker/docker/api/server/middleware.VersionMiddleware.WrapHandler.func1+1531\n        github.com/docker/docker/pkg/authorization.(*Middleware).WrapHandler.func1+2086\n        local.github.com/docker/docker/api/server.(*Server).makeHTTPHandler.func1+577\n        net/http.HandlerFunc.ServeHTTP+70\n        github.com/docker/docker/vendor/github.com/gorilla/mux.(*Router).ServeHTTP+228\n        net/http.serverHandler.ServeHTTP+166\n        net/http.(*conn).serve+2167\n        runtime.goexit+1\n\n200: https://auth.docker.io/token\n\n        net/http.(*Client).do+1225\n        github.com/docker/docker/vendor/github.com/docker/distribution/registry/client/auth.(*tokenHandler).fetchToken+710\n        local.github.com/docker/docker/vendor/github.com/docker/distribution/registry/client/auth.(*tokenHandler).getToken+859\n        github.com/docker/docker/vendor/github.com/docker/distribution/registry/client/auth.(*tokenHandler).AuthorizeRequest+146\n        github.com/docker/docker/vendor/github.com/docker/distribution/registry/client/auth.(*endpointAuthorizer).ModifyRequest+793\n        github.com/docker/docker/vendor/github.com/docker/distribution/registry/client/transport.(*transport).RoundTrip+137\n        local.net/http.send+1093\n        local.net/http.(*Client).send+252\n        net/http.(*Client).do+976\n        github.com/docker/docker/vendor/github.com/docker/distribution/registry/client.(*tags).Get.func1+451\n        local.github.com/docker/docker/vendor/github.com/docker/distribution/registry/client.(*tags).Get+395\n        github.com/docker/docker/distribution.(*v2Puller).pullV2Tag+6799\n        github.com/docker/docker/distribution.(*v2Puller).pullV2Repository+889\n        github.com/docker/docker/distribution.(*v2Puller).Pull+784\n        github.com/docker/docker/distribution.Pull+1525\n        github.com/docker/docker/daemon/images.(*ImageService).pullImageWithReference+1345\n        github.com/docker/docker/daemon/images.(*ImageService).PullImage+350\n        local.github.com/docker/docker/api/server/router/image.(*imageRouter).postImagesCreate+1666\n        github.com/docker/docker/api/server/router/image.(*imageRouter).postImagesCreate-fm+107\n        github.com/docker/docker/api/server/middleware.ExperimentalMiddleware.WrapHandler.func1+375\n        local.github.com/docker/docker/api/server/middleware.VersionMiddleware.WrapHandler.func1+1531\n        github.com/docker/docker/pkg/authorization.(*Middleware).WrapHandler.func1+2086\n        local.github.com/docker/docker/api/server.(*Server).makeHTTPHandler.func1+577\n        net/http.HandlerFunc.ServeHTTP+70\n        github.com/docker/docker/vendor/github.com/gorilla/mux.(*Router).ServeHTTP+228\n        net/http.serverHandler.ServeHTTP+166\n        net/http.(*conn).serve+2167\n        runtime.goexit+1\n\n200: https://auth.docker.io/token\n\n        net/http.(*Client).do+1225\n        local.github.com/docker/docker/vendor/github.com/docker/distribution/registry/client.(*tags).Get+395\n        github.com/docker/docker/distribution.(*v2Puller).pullV2Tag+6799\n        github.com/docker/docker/distribution.(*v2Puller).pullV2Repository+889\n        github.com/docker/docker/distribution.(*v2Puller).Pull+784\n        github.com/docker/docker/distribution.Pull+1525\n        github.com/docker/docker/daemon/images.(*ImageService).pullImageWithReference+1345\n        github.com/docker/docker/daemon/images.(*ImageService).PullImage+350\n        local.github.com/docker/docker/api/server/router/image.(*imageRouter).postImagesCreate+1666\n        github.com/docker/docker/api/server/router/image.(*imageRouter).postImagesCreate-fm+107\n        github.com/docker/docker/api/server/middleware.ExperimentalMiddleware.WrapHandler.func1+375\n        local.github.com/docker/docker/api/server/middleware.VersionMiddleware.WrapHandler.func1+1531\n        github.com/docker/docker/pkg/authorization.(*Middleware).WrapHandler.func1+2086\n        local.github.com/docker/docker/api/server.(*Server).makeHTTPHandler.func1+577\n        net/http.HandlerFunc.ServeHTTP+70\n        github.com/docker/docker/vendor/github.com/gorilla/mux.(*Router).ServeHTTP+228\n        net/http.serverHandler.ServeHTTP+166\n        net/http.(*conn).serve+2167\n        runtime.goexit+1\n```\n\n# Getting Symbol Names\n\nRun ```readelf -a --wide target``` to get all the symbols in your target.\n\n# Tracing Programs In Docker Containers\n\nSay that the target is /bin/foo in a container with pid 123. Use\n\n\n```\n/proc/123/root/bin/foo\n\n```\n\nas the target executable.\n\n# Roll Your Own Templates\n\nSee the `templates` directory for examples on how to create templates. These are golang\n[text templates](https://pkg.go.dev/text/template). The templates can make use of the following\n\n\n* `.ExePath` gives the absolute path of the target executable\n* `.Arguments` gives access to the key-value pairs given on the command line\n* `.RegsABI` is true if argument passing with registers is enabled\n\n\n\n# Limitations\n\n* Only works on x86-64\n* Requires target to be built with golang \u003e= 1.17 for full functionality. Some scripts will not work without the register based calling convention.\n* short lived programs may have stack traces which are only hex addresses. See [this](https://github.com/iovisor/bpftrace/issues/246) bug\n\n# TODO\n\n* Tutorial\n* Examples of more complex scripts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevenjohnstone%2Fgo-bpf-gen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevenjohnstone%2Fgo-bpf-gen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevenjohnstone%2Fgo-bpf-gen/lists"}