A function tracer to boost your debugging

Overview

tgo: a function tracer to boost your debugging

GoDoc Build Status Go Report Card

Examples

In this example, the functions called between tracer.Start() and tracer.Stop() are traced.

package main

import (
	"fmt"
	"os"
	"strconv"

	"github.com/ks888/tgo/lib/tracer"
)

func fib(n int) int {
	if n == 0 || n == 1 {
		return n
	}
	return fib(n-1) + fib(n-2)
}

func main() {
	tracer.Start()

	var n int64
	if len(os.Args) > 1 {
		n, _ = strconv.ParseInt(os.Args[1], 10, 64)
	}
	val := fib(int(n))
	fmt.Println(val)

	tracer.Stop()
}

When you run the program, the trace logs are printed:

% go build fib.go
% ./fib 3
\ (#01) strconv.ParseInt(s = "3", base = 10, bitSize = 64) (...)
/ (#01) strconv.ParseInt(s = "3", base = 10, bitSize = 64) (i = 3, err = nil)
\ (#01) main.fib(n = 3) (...)
/ (#01) main.fib(n = 3) (~r1 = 2)
\ (#01) fmt.Println(a = []{int(2)}) (...)
2
/ (#01) fmt.Println(a = []{int(2)}) (n = 2, err = nil)

Now let's use the tgo to boost your debugging. The example below sorts the int slice using the quicksort, but it has the bug.

package main

import (
	"fmt"

	"github.com/ks888/tgo/lib/tracer"
)

func qsort(data []int, start, end int) {
	if end-start <= 1 {
		return
	}

	index := partition(data, start, end)
	qsort(data, start, index-1)
	qsort(data, index, end)
	return
}

func partition(data []int, start, end int) int {
	pivot := data[(start+end)/2]
	left, right := start, end
	for {
		for left <= end && data[left] < pivot {
			left++
		}
		for right >= start && data[right] > pivot {
			right--
		}
		if left > right {
			return left
		}

		data[left], data[right] = data[right], data[left]
		left++
		right--
	}
}

func main() {
	tracer.SetTraceLevel(2) // will be explained later
	tracer.Start()

	testdata := []int{3, 1, 2, 5, 4}
	qsort(testdata, 0, len(testdata)-1)

	tracer.Stop()

	fmt.Println(testdata)
}

The result is [2 1 3 4 5], which is obviously not sorted.

% go build qsort.go
% ./qsort
\ (#01) main.qsort(data = []{3, 1, 2, 5, 4}, start = 0, end = 4) ()
|\ (#01) main.partition(data = []{3, 1, 2, 5, 4}, start = 0, end = 4) (...)
|/ (#01) main.partition(data = []{2, 1, 3, 5, 4}, start = 0, end = 4) (~r3 = 2)
|\ (#01) main.qsort(data = []{2, 1, 3, 5, 4}, start = 0, end = 1) ()
|/ (#01) main.qsort(data = []{2, 1, 3, 5, 4}, start = 0, end = 1) ()
|\ (#01) main.qsort(data = []{2, 1, 3, 5, 4}, start = 2, end = 4) ()
|/ (#01) main.qsort(data = []{2, 1, 3, 4, 5}, start = 2, end = 4) ()
/ (#01) main.qsort(data = []{2, 1, 3, 4, 5}, start = 0, end = 4) ()
[2 1 3 4 5]

Now the trace logs help you find a bug.

  1. The 1st line of the logs shows main.qsort is called properly. main.qsort sorts the data between start and end (inclusive).
  2. 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.
  3. The 4th and 5th lines are ... strange. start is 0 and end is 1, but the data between 0 and 1 is not sorted.

Let'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 <= 1 should be end-start <= 0. We found the bug.

It'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.

Features

  • As easy as print debug: just insert 1-2 line(s) to your code
  • But more powerful: visually tells you what's actually going on in your code
  • Works without debugging info
    • It's actually important for testing. See the usage below for more details.
  • Support Linux/Mac OS X

Install

Note: supported go version is 1.10 or later.

Mac OS X

Install the tgo binary and its library:

go get -u github.com/ks888/tgo/cmd/tgo

If you have an error, you may need to install Xcode Command Line Tools: xcode-select --install

Linux

Install the tgo binary and its library:

go get -u github.com/ks888/tgo/cmd/tgo

The tgo binary attaches to your process using the ptrace mechanism. To enable this, run the command below:

sudo sh -c 'echo 0 > /proc/sys/kernel/yama/ptrace_scope'

If you run the program in the docker container, add the --cap-add sys_ptrace option. For example:

docker run --cap-add sys_ptrace -it golang:1 /bin/bash

Windows

Not supported.

Usage

Basically you just need to specify the starting point with tracer.Start() and the ending point with tracer.Stop().

package main

import (
	"fmt"

	"github.com/ks888/tgo/lib/tracer"
)

func fib(n int) int {
	if n == 0 || n == 1 {
		return n
	}
	return fib(n-1) + fib(n-2)
}

func main() {
	tracer.Start()

	val := fib(3)
	fmt.Println(val)

	tracer.Stop()
}

When 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.

% go build simple.go
% ./simple
\ (#01) main.fib(n = 3) (...)
/ (#01) main.fib(n = 3) (~r1 = 2)
\ (#01) fmt.Println(a = []{int(2)}) (...)
2
/ (#01) fmt.Println(a = []{int(2)}) (n = 2, err = nil)

All the examples in this doc are available in the _examples directory. If this example doesn't work, check the error value tracer.Start() returns.

Set TraceLevel

You 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().

package main

import (
	"fmt"

	"github.com/ks888/tgo/lib/tracer"
)

func fib(n int) int {
	if n == 0 || n == 1 {
		return n
	}
	return fib(n-1) + fib(n-2)
}

func main() {
	tracer.SetTraceLevel(2)
	tracer.Start()

	val := fib(3)
	fmt.Println(val)

	tracer.Stop()
}

In this example, the trace level is 2. Because the default level is 1, now the trace logs go one level deeper.

% go build tracelevel.go
% ./tracelevel
\ (#01) main.fib(n = 3) (...)
|\ (#01) main.fib(n = 2) (...)
|/ (#01) main.fib(n = 2) (~r1 = 1)
|\ (#01) main.fib(n = 1) (...)
|/ (#01) main.fib(n = 1) (~r1 = 1)
/ (#01) main.fib(n = 3) (~r1 = 2)
\ (#01) fmt.Println(a = []{int(2)}) (...)
|\ (#01) fmt.Fprintln(a = -, w = -) (...)
2
|/ (#01) fmt.Fprintln(a = -, w = -) (n = 2, err = nil)
/ (#01) fmt.Println(a = []{int(2)}) (n = 2, err = nil)

Note 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.

% GOFLAGS="-gcflags=all=-N" go build tracelevel.go

Works without debugging info

If 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:

package main

import (
	"testing"

	"github.com/ks888/tgo/lib/tracer"
)

func TestFib(t *testing.T) {
	tracer.Start()
	actual := fib(3)
	tracer.Stop()

	if actual != 2 {
		t.Errorf("wrong: %v", actual)
	}
}
% go test -v fib.go fib_test.go
=== RUN   TestFib
\ (#06) command-line-arguments.fib(0x3, 0x0) ()
/ (#06) command-line-arguments.fib(0x3, 0x2) ()
--- PASS: TestFib (0.46s)
PASS
ok      command-line-arguments  0.485s

Functions 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.

If 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:

% GOFLAGS="-ldflags=-w=false" go test -v fib.go fib_test.go
=== RUN   TestFib
\ (#18) command-line-arguments.fib(n = 3) (...)
/ (#18) command-line-arguments.fib(n = 3) (~r1 = 2)
--- PASS: TestFib (0.55s)
PASS
ok      command-line-arguments  0.578s

Note that GOFLAGS is not supported in go 1.10 or earlier.

Tips

There are some random tips:

  • There are more options to change the tgo's behaviors. See the godoc for details.
  • When a go routine calls tracer.Start(), it means only that go routine is traced. Other go routines are not affected.
    • Similarly, tracer.Stop() just stops the tracing of the go routine which called that function.
  • Builtin functions are not traced. These functions are usually replaced with runtime package functions or assembly instructions.
Owner
Kishin Yagami
Kishin Yagami
A toy ray tracer to practice writing go.

pbrt-go A toy ray tracer written to practice writing go. Slowly working towards an implementation based on the excellent 3rd edition of PBRT, because

Dan Bolan 0 Oct 19, 2021
Cloud function + website for resizing, cropping and bordering images for pragalicious.com

Cloud function + website for resizing, cropping and bordering images for pragalicious.com

Ard Scheirlynck 0 Jan 23, 2022
:football: Generate your own O'RLY animal book cover to troll your colleagues | 生成你自己的O'RLY动物书封面,让你的同事惊掉下巴

English | 中文 O'RLY Cover Generator O'RLY Cover Generator is a parody book cover generator, implemented in Golang and Vue.js, supporting a wide range o

LI Zhennan 650 Jul 29, 2022
Publish Your GIS Data(Vector Data) to PostGIS and Geoserver

GISManager Publish Your GIS Data(Vector Data) to PostGIS and Geoserver How to install: go get -v github.com/hishamkaram/gismanager Usage: testdata fol

Hisham waleed karam 45 Jun 16, 2022
Generate a TwitterCard(OGP) image for your Hugo posts.

Twitter Card Image Generator Generate Twitter card (OGP) images for your blog posts. Supported front-matters are title, author, categories, tags, and

Aya Igarashi 127 Aug 9, 2022
🛰️ Clone all your starred GitHub repos

solar ??️ Clone all your starred GitHub repos solar ❓ What is solar? ?? Install ?? macOS ??️ Linux and Windows ?? Commands ??️ solar download ?? Contr

Matt Gleich 31 Mar 5, 2022
📸 Clean your image folder using perceptual hashing and BK-trees using Go!

Image Cleaner ?? ?? ➡ ?? This tool can take your image gallery and create a new folder with image-alike-cluster folders. It uses a perceptual image ha

lord_santanna 7 Jun 15, 2022
Radius parsing in golang using gopacket. You can parse from either live traffic or from pcap of your choice.

go-radius Radius parsing in golang using gopacket. You can parse from either live traffic or from pcap of your choice. RADIUS RADIUS is an AAA (authen

Adeel Khan 4 Dec 27, 2021
Go-tracer - Golang simple tracer client

tracer easy tracer api for jaeger Feature: support component: http middleware gr

fengyun.rui 31 Jun 16, 2022
ORM for Cloud Spanner to boost your productivity

ORM for Cloud Spanner to boost your productivity ??

Kanji Hirata 18 Jun 17, 2022
gopkg is a universal utility collection for Go, it complements offerings such as Boost, Better std, Cloud tools.

gopkg is a universal utility collection for Go, it complements offerings such as Boost, Better std, Cloud tools. Table of Contents Introduction

Bytedance Inc. 749 Aug 12, 2022
Mev-boost: A middleware server written in Go

mev-boost A middleware server written in Go, that sits between an ethereum PoS consensus client and an execution client. It allows consensus clients t

Flashbots 334 Aug 11, 2022
A path tracer written in Go.

pt: a golang path tracer This is a CPU-only, unidirectional path tracing engine written in Go. It has lots of features and a simple API. Features Supp

Michael Fogleman 2k Aug 6, 2022
Experimental system call tracer for Linux x86-64, written in Go

gtrace A system call tracer for Linux x86-64. DISCLAIMER: This software is experimental and not considered stable. Do not use it in mission-critical e

Agis Anastasopoulos 74 Mar 28, 2022
Jenkins tracer is used to record all the Jenkins job environment variables and metrics, and send them to Elasticsearch

Jenkins Tracer Jenkins tracer is used to record all the jenkins job variables like record the build duration, build variables, repository metadata, et

Misbahul Ardani 3 Apr 22, 2021
A toy ray tracer to practice writing go.

pbrt-go A toy ray tracer written to practice writing go. Slowly working towards an implementation based on the excellent 3rd edition of PBRT, because

Dan Bolan 0 Oct 19, 2021
ECS task event/log tracer CLI

tracer tracer is a tracing tool for Amazon ECS tasks. tracer shows events and logs of the tasks order by timestamp. example Run a task successfully an

FUJIWARA Shunichiro 57 Jul 23, 2022
First attempt to trace a shell script with Datadog's go tracer

dd-trace-shell First attempt to trace a shell script with Datadog's go tracer. W

Barbayar Dashzeveg 0 Dec 17, 2021
A simple application to show how to use dd-trace-go's tracer and profiler.

dd-trace-go-demo A simple application to show how to use dd-trace-go's tracer and profiler. Usage To run this demo application, simply clone this repo

Felix Geisendörfer 3 Mar 2, 2022
Goal is to generate logger and tracer wraps around a certain struct

Goal is to generate logger and tracer wraps around a certain struct

null 0 Feb 13, 2022
Implements a deep pretty printer for Go data structures to aid in debugging

go-spew Go-spew implements a deep pretty printer for Go data structures to aid in debugging. A comprehensive suite of tests with 100% test coverage is

Dave Collins 5.2k Aug 8, 2022
webbased debugging for Go programs

Hopwatch, a debugging tool for Go Hopwatch is a simple tool in HTML5 that can help debug Go programs. It works by communicating to a WebSockets based

Ernest Micklei 256 Jul 4, 2022
Reduce debugging time while programming Go. Use static and stack-trace analysis to determine which func call causes the error.

Errlog: reduce debugging time while programming Introduction Use errlog to improve error logging and speed up debugging while you create amazing code

Martin Joly 407 Jul 27, 2022
Implements a deep pretty printer for Go data structures to aid in debugging

go-spew Go-spew implements a deep pretty printer for Go data structures to aid in debugging. A comprehensive suite of tests with 100% test coverage is

Dave Collins 5.2k Aug 3, 2022
A generic fuzzing and delta-debugging framework

Tavor Tavor (Sindarin for woodpecker) is a framework for easily implementing and using fuzzing and delta-debugging. Its EBNF-like notation allows you

Markus Zimmermann 235 May 31, 2022
A suite of gRPC debugging tools. Like Fiddler/Charles but for gRPC.

grpc-tools A suite of tools for gRPC debugging and development. Like Fiddler/Charles but for gRPC! The main tool is grpc-dump which transparently inte

Bradley Kemp 1.1k Aug 10, 2022
Quick and dirty debugging output for tired Go programmers

q q is a better way to do print statement debugging. Type q.Q instead of fmt.Printf and your variables will be printed like this: Why is this better t

Ryan Boehning 1.4k Jul 26, 2022
Litter is a pretty printer library for Go data structures to aid in debugging and testing.

Litter Litter is a pretty printer library for Go data structures to aid in debugging and testing. Litter is provided by Sanity: The Headless CMS Const

Sanity 1.3k Aug 2, 2022
HTTP, HTTP2, HTTPS, Websocket debugging proxy

English | 简体中文 We recommend updating whistle and Node to ensure that you receive important features, bugfixes and performance improvements. Some versi

avenwu 10.8k Aug 8, 2022