{"id":37219440,"url":"https://github.com/ks888/tgo","last_synced_at":"2026-01-15T01:16:32.038Z","repository":{"id":83696415,"uuid":"154982032","full_name":"ks888/tgo","owner":"ks888","description":"A function tracer to boost your debugging","archived":false,"fork":false,"pushed_at":"2019-01-30T13:52:16.000Z","size":4330,"stargazers_count":71,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-08-14T21:16:33.759Z","etag":null,"topics":["callgraph","developer-tools","function-tracer","go","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ks888.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-27T15:54:43.000Z","updated_at":"2023-05-08T17:26:11.000Z","dependencies_parsed_at":"2023-07-21T06:15:32.169Z","dependency_job_id":null,"html_url":"https://github.com/ks888/tgo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ks888/tgo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ks888%2Ftgo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ks888%2Ftgo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ks888%2Ftgo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ks888%2Ftgo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ks888","download_url":"https://codeload.github.com/ks888/tgo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ks888%2Ftgo/sbom","scorecard":{"id":571107,"data":{"date":"2025-08-11","repo":{"name":"github.com/ks888/tgo","commit":"80bf8940729288c623669c88f69f68158a02a32d"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-20T16:22:57.316Z","repository_id":83696415,"created_at":"2025-08-20T16:22:57.316Z","updated_at":"2025-08-20T16:22:57.316Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28441027,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T00:55:22.719Z","status":"ssl_error","status_checked_at":"2026-01-15T00:55:20.945Z","response_time":107,"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":["callgraph","developer-tools","function-tracer","go","golang"],"created_at":"2026-01-15T01:16:31.279Z","updated_at":"2026-01-15T01:16:32.024Z","avatar_url":"https://github.com/ks888.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tgo: a function tracer to boost your debugging\n\n[![GoDoc](https://godoc.org/github.com/ks888/tgo?status.svg)](https://godoc.org/github.com/ks888/tgo/lib/tracer)\n[![Build Status](https://travis-ci.com/ks888/tgo.svg?branch=master)](https://travis-ci.com/ks888/tgo)\n[![Go Report Card](https://goreportcard.com/badge/github.com/ks888/tgo)](https://goreportcard.com/report/github.com/ks888/tgo)\n\n### Examples\n\nIn this example, the functions called between `tracer.Start()` and `tracer.Stop()` are traced.\n\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/ks888/tgo/lib/tracer\"\n)\n\nfunc fib(n int) int {\n\tif n == 0 || n == 1 {\n\t\treturn n\n\t}\n\treturn fib(n-1) + fib(n-2)\n}\n\nfunc main() {\n\ttracer.Start()\n\n\tvar n int64\n\tif len(os.Args) \u003e 1 {\n\t\tn, _ = strconv.ParseInt(os.Args[1], 10, 64)\n\t}\n\tval := fib(int(n))\n\tfmt.Println(val)\n\n\ttracer.Stop()\n}\n```\n\nWhen you run the program, the trace logs are printed:\n\n```shell\n% go build fib.go\n% ./fib 3\n\\ (#01) strconv.ParseInt(s = \"3\", base = 10, bitSize = 64) (...)\n/ (#01) strconv.ParseInt(s = \"3\", base = 10, bitSize = 64) (i = 3, err = nil)\n\\ (#01) main.fib(n = 3) (...)\n/ (#01) main.fib(n = 3) (~r1 = 2)\n\\ (#01) fmt.Println(a = []{int(2)}) (...)\n2\n/ (#01) fmt.Println(a = []{int(2)}) (n = 2, err = nil)\n```\n\nNow let's use the tgo to boost your debugging. The example below sorts the int slice using the quicksort, *but it has the bug*.\n\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ks888/tgo/lib/tracer\"\n)\n\nfunc qsort(data []int, start, end int) {\n\tif end-start \u003c= 1 {\n\t\treturn\n\t}\n\n\tindex := partition(data, start, end)\n\tqsort(data, start, index-1)\n\tqsort(data, index, end)\n\treturn\n}\n\nfunc partition(data []int, start, end int) int {\n\tpivot := data[(start+end)/2]\n\tleft, right := start, end\n\tfor {\n\t\tfor left \u003c= end \u0026\u0026 data[left] \u003c pivot {\n\t\t\tleft++\n\t\t}\n\t\tfor right \u003e= start \u0026\u0026 data[right] \u003e pivot {\n\t\t\tright--\n\t\t}\n\t\tif left \u003e right {\n\t\t\treturn left\n\t\t}\n\n\t\tdata[left], data[right] = data[right], data[left]\n\t\tleft++\n\t\tright--\n\t}\n}\n\nfunc main() {\n\ttracer.SetTraceLevel(2) // will be explained later\n\ttracer.Start()\n\n\ttestdata := []int{3, 1, 2, 5, 4}\n\tqsort(testdata, 0, len(testdata)-1)\n\n\ttracer.Stop()\n\n\tfmt.Println(testdata)\n}\n```\n\nThe result is `[2 1 3 4 5]`, which is obviously not sorted.\n\n```shell\n% go build qsort.go\n% ./qsort\n\\ (#01) main.qsort(data = []{3, 1, 2, 5, 4}, start = 0, end = 4) ()\n|\\ (#01) main.partition(data = []{3, 1, 2, 5, 4}, start = 0, end = 4) (...)\n|/ (#01) main.partition(data = []{2, 1, 3, 5, 4}, start = 0, end = 4) (~r3 = 2)\n|\\ (#01) main.qsort(data = []{2, 1, 3, 5, 4}, start = 0, end = 1) ()\n|/ (#01) main.qsort(data = []{2, 1, 3, 5, 4}, start = 0, end = 1) ()\n|\\ (#01) main.qsort(data = []{2, 1, 3, 5, 4}, start = 2, end = 4) ()\n|/ (#01) main.qsort(data = []{2, 1, 3, 4, 5}, start = 2, end = 4) ()\n/ (#01) main.qsort(data = []{2, 1, 3, 4, 5}, start = 0, end = 4) ()\n[2 1 3 4 5]\n```\n\nNow the trace logs help you find a bug.\n\n1. The 1st line of the logs shows `main.qsort` is called properly. `main.qsort` sorts the `data` between `start` and `end` (inclusive).\n2. The 2nd and 3rd lines tell us `main.partition` works as expected here. `main.partition` partitions the `data` between `start` and `end` (inclusive) and returns the pivot index.\n3. The 4th and 5th lines are ... strange. `start` is 0 and `end` is 1, but the `data` between 0 and 1 is not sorted.\n\nLet's see the implementation of `main.qsort`, assuming `start` is 0 and `end` is 1. It becomes apparent the `if` statement at the 1st line has the off-by-one error. `end-start \u003c= 1` should be `end-start \u003c= 0`. We found the bug.\n\nIt's possible to use print debugging instead, but we may need to scatter print functions and run the code again and again. Also, it's likely the logs are less structured. The tgo approach is faster and clearer and so can boost your debugging.\n\n### Features\n\n* As easy as print debug: just insert 1-2 line(s) to your code\n* But more powerful: visually tells you what's actually going on in your code\n* Works without debugging info\n  * It's actually important for testing. See the usage below for more details.\n* Support Linux/Mac OS X\n\n### Install\n\n*Note: supported go version is 1.10 or later.*\n\n#### Mac OS X\n\nInstall the `tgo` binary and its library:\n\n```\ngo get -u github.com/ks888/tgo/cmd/tgo\n```\n\nIf you have an error, you may need to install Xcode Command Line Tools: `xcode-select --install`\n\n#### Linux\n\nInstall the `tgo` binary and its library:\n\n```\ngo get -u github.com/ks888/tgo/cmd/tgo\n```\n\nThe `tgo` binary attaches to your process using the ptrace mechanism. To enable this, run the command below:\n\n```\nsudo sh -c 'echo 0 \u003e /proc/sys/kernel/yama/ptrace_scope'\n```\n\nIf you run the program in the docker container, add the `--cap-add sys_ptrace` option. For example:\n\n```\ndocker run --cap-add sys_ptrace -it golang:1 /bin/bash\n```\n\n#### Windows\n\nNot supported.\n\n### Usage\n\nBasically you just need to specify the starting point with `tracer.Start()` and the ending point with `tracer.Stop()`.\n\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ks888/tgo/lib/tracer\"\n)\n\nfunc fib(n int) int {\n\tif n == 0 || n == 1 {\n\t\treturn n\n\t}\n\treturn fib(n-1) + fib(n-2)\n}\n\nfunc main() {\n\ttracer.Start()\n\n\tval := fib(3)\n\tfmt.Println(val)\n\n\ttracer.Stop()\n}\n```\n\nWhen you build and run this program, you can see the function trace logs of `fib()` and `fmt.Println()`. Though `fib()` is called recursively, only the 1st call's log is printed.\n\n```shell\n% go build simple.go\n% ./simple\n\\ (#01) main.fib(n = 3) (...)\n/ (#01) main.fib(n = 3) (~r1 = 2)\n\\ (#01) fmt.Println(a = []{int(2)}) (...)\n2\n/ (#01) fmt.Println(a = []{int(2)}) (n = 2, err = nil)\n```\n\nAll the examples in this doc are available in the `_examples` directory. If this example doesn't work, check the error value `tracer.Start()` returns.\n\n#### Set TraceLevel\n\nYou often need a little deeper trace logs. For example, you may want to know the functions `fib()` and `fmt.Println()` call internally. You can do that by adjusting the trace level with `tracer.SetTraceLevel()`.\n\n```golang\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ks888/tgo/lib/tracer\"\n)\n\nfunc fib(n int) int {\n\tif n == 0 || n == 1 {\n\t\treturn n\n\t}\n\treturn fib(n-1) + fib(n-2)\n}\n\nfunc main() {\n\ttracer.SetTraceLevel(2)\n\ttracer.Start()\n\n\tval := fib(3)\n\tfmt.Println(val)\n\n\ttracer.Stop()\n}\n```\n\nIn this example, the trace level is 2. Because the default level is 1, now the trace logs go one level deeper.\n\n```shell\n% go build tracelevel.go\n% ./tracelevel\n\\ (#01) main.fib(n = 3) (...)\n|\\ (#01) main.fib(n = 2) (...)\n|/ (#01) main.fib(n = 2) (~r1 = 1)\n|\\ (#01) main.fib(n = 1) (...)\n|/ (#01) main.fib(n = 1) (~r1 = 1)\n/ (#01) main.fib(n = 3) (~r1 = 2)\n\\ (#01) fmt.Println(a = []{int(2)}) (...)\n|\\ (#01) fmt.Fprintln(a = -, w = -) (...)\n2\n|/ (#01) fmt.Fprintln(a = -, w = -) (n = 2, err = nil)\n/ (#01) fmt.Println(a = []{int(2)}) (n = 2, err = nil)\n```\n\nNote that the input args of `fmt.Fprintln` is `-` (not available) here. It's likely the debugging info are omitted due to optimization. To see the complete result, set `\"-gcflags=-N\"` (fast, but may not complete) or `\"-gcflags=all=-N\"` (slow, but complete) to `GOFLAGS` environment variable.\n\n```shell\n% GOFLAGS=\"-gcflags=all=-N\" go build tracelevel.go\n```\n\n#### Works without debugging info\n\nIf you run the program with `go test` or `go run`, debugging info, such as DWARF data, are dropped. Fortunately, tgo works even in such a case. Let's trace the test for the fib function:\n\n```golang\npackage main\n\nimport (\n\t\"testing\"\n\n\t\"github.com/ks888/tgo/lib/tracer\"\n)\n\nfunc TestFib(t *testing.T) {\n\ttracer.Start()\n\tactual := fib(3)\n\ttracer.Stop()\n\n\tif actual != 2 {\n\t\tt.Errorf(\"wrong: %v\", actual)\n\t}\n}\n```\n\n```\n% go test -v fib.go fib_test.go\n=== RUN   TestFib\n\\ (#06) command-line-arguments.fib(0x3, 0x0) ()\n/ (#06) command-line-arguments.fib(0x3, 0x2) ()\n--- PASS: TestFib (0.46s)\nPASS\nok      command-line-arguments  0.485s\n```\n\nFunctions are traced as usual, but args are the entire args value, divided by pointer size. In this example, `\\ (#06) command-line-arguments.fib(0x3, 0x0) ()` indicates 1st input args is `0x3` and the initial return value is `0x0`. Because it's difficult to separate input args from the return args without debugging info, all args are shown as the input args. This format imitates the stack trace shown in the case of panic.\n\nIf you want to show the args as usual, set `\"-ldflags=-w=false\"` to `GOFLAGS` environment variable so that the debugging info is included in the binary. For example:\n\n```\n% GOFLAGS=\"-ldflags=-w=false\" go test -v fib.go fib_test.go\n=== RUN   TestFib\n\\ (#18) command-line-arguments.fib(n = 3) (...)\n/ (#18) command-line-arguments.fib(n = 3) (~r1 = 2)\n--- PASS: TestFib (0.55s)\nPASS\nok      command-line-arguments  0.578s\n```\n\nNote that GOFLAGS is not supported in go 1.10 or earlier.\n\n#### Tips\n\nThere are some random tips:\n* There are more options to change the tgo's behaviors. See the [godoc](https://godoc.org/github.com/ks888/tgo/lib/tracer) for details.\n* When a go routine calls `tracer.Start()`, it means only that go routine is traced. Other go routines are not affected.\n  * Similarly, `tracer.Stop()` just stops the tracing of the go routine which called that function.\n* Builtin functions are not traced. These functions are usually replaced with `runtime` package functions or assembly instructions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fks888%2Ftgo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fks888%2Ftgo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fks888%2Ftgo/lists"}