Golang
Porting from bash to Go
My primary methodology when porting from bash to Go is to port a function at a time. I generally pick leaf functions first and iterate "inwards" until major functionality is ported. If we do this well, we should be able to have the intermediate functions ported on master without having to port the main functionality.
In theory I could write tests for this as I go, but I am a moster so I don't. Instead, I tend to modify the bash to call the bash version of the function, run it, and then call the go version of the function and compare.
So for example I just ported the generated_go_files_in_packages
function. To
call the go version I run:
$ go run ./bin/_gozr generated_go_files_in_packages ./common/go
For the bash version I put something like this after all the functions are defined but before any of the main handling code runs:
generated_go_files_in_packages "$@"
exit 1
From there I can iterate and make sure output matches closely enough to make progress.
Reference
To look up a built in function you can do:
$ go doc builtin.append
Get assembly of a function:
$ go tool objdump -S -s main.example ./example
To clarify what's happening in a trace, use the trace api
To track coverage of tests:
go test -coverprofile=cover.out
Convert to raw bytes:
package main
import (
"fmt"
"unsafe"
)
func main() {
i := 1
const sz = unsafe.Sizeof(i)
fmt.Println("raw", i, "size", sz)
name := (*(*[sz]byte)(unsafe.Pointer(&i)))[:]
fmt.Print("0b")
for i, b := range name {
fmt.Printf("%08b", b)
if i != len(name)-1 {
fmt.Print(" ")
}
}
fmt.Println()
fmt.Print("0x")
for _, b := range name {
fmt.Printf("%0x", b)
}
fmt.Println()
}
Assembly
- A Manual for the Plan 9 assembler
- A Quick Guide to Go's Assembler - The Go Programming Language
- Assembly - goroutines
- The Art of Assembly Language - Art Of Intel x86 Assembly.pdf
- Assembly Language Step-by-Step: Programming with Linux 3, Duntemann, Jeff, eBook - Amazon.com
- x86 and amd64 instruction reference
Example:
main.go:
package main
func X(i float64) float64
X.s:
#include "textflag.h"
// func X(x float64) float64
TEXT ·X(SB),NOSPLIT,$0
FMOVD x+0(FP),F0
FSQRT
FMOVDP F0,ret+8(FP)
RET
reflect
Value Walk
package main
import (
"fmt"
"reflect"
"strings"
)
func main() {
var x struct {
Foo string
Bar struct {
Baz string
}
Bong [3]int
Biff *string
Bork map[int]int
Ch chan string
Sl []string
F func(x, y, z int) (w, t, e error)
}
x.Foo = "a"
x.Bork = map[int]int{1: 2, 3: 4}
x.Sl = []string{"p", "o", "i"}
valueWalk(x)
}
func valueWalk(val interface{}) error {
return rValueWalk(reflect.ValueOf(val), 1)
}
func tabs(depth int) {
fmt.Print(strings.Repeat("\t", depth))
}
func rValueWalk(rVal reflect.Value, depth int) error {
switch rVal.Kind() {
case reflect.Invalid:
fmt.Print("invalid")
case reflect.Bool:
fmt.Print(rVal.Bool())
case reflect.Int:
fmt.Print(rVal.Int())
case reflect.Int8:
fmt.Print(rVal.Int())
case reflect.Int16:
fmt.Print(rVal.Int())
case reflect.Int32:
fmt.Print(rVal.Int())
case reflect.Int64:
fmt.Print(rVal.Int())
case reflect.Uint:
fmt.Print(rVal.Uint())
case reflect.Uint8:
fmt.Print(rVal.Uint())
case reflect.Uint16:
fmt.Print(rVal.Uint())
case reflect.Uint32:
fmt.Print(rVal.Uint())
case reflect.Uint64:
fmt.Print(rVal.Uint())
case reflect.Uintptr:
fmt.Print(rVal.Uint())
case reflect.Float32:
fmt.Print(rVal.Float())
case reflect.Float64:
fmt.Print(rVal.Float())
case reflect.Complex64:
fmt.Print(rVal.Complex())
case reflect.Complex128:
fmt.Print(rVal.Complex())
case reflect.Array:
fmt.Print("[", rVal.Len(), "]{")
for i := 0; i < rVal.Len(); i++ {
rValueWalk(rVal.Index(i), depth+1)
if i != rVal.Len()-1 {
fmt.Print(", ")
}
}
fmt.Print("}")
case reflect.Chan:
fmt.Print("won't pretty print chan contents")
case reflect.Func:
fmt.Print("won't pretty print func contents")
case reflect.Interface:
fmt.Print("won't pretty print interface contents")
case reflect.Map:
fmt.Print("map[")
fmt.Print("]{")
keys := rVal.MapKeys()
for i, k := range keys {
v := rVal.MapIndex(k)
rValueWalk(k, depth+1)
fmt.Print(": ")
rValueWalk(v, depth+1)
if i != len(keys)-1 {
fmt.Print(", ")
}
}
fmt.Println("}")
case reflect.Ptr:
fmt.Printf("%x", rVal.Pointer())
case reflect.Slice:
fmt.Print("[]{")
for i := 0; i < rVal.Len(); i++ {
rValueWalk(rVal.Index(i), depth+1)
if i != rVal.Len()-1 {
fmt.Print(", ")
}
}
fmt.Println("}")
case reflect.String:
fmt.Printf("%q", rVal.String())
case reflect.Struct:
t := rVal.Type()
fmt.Println("struct {")
fieldCount := rVal.NumField()
for i := 0; i < fieldCount; i++ {
f := rVal.Field(i)
fT := t.Field(i)
tabs(depth)
fmt.Print(fT.Name, ": ")
rValueWalk(f, depth+1)
fmt.Println(",")
}
tabs(depth - 1)
fmt.Print("}")
case reflect.UnsafePointer:
fmt.Print("unsafe.Pointer")
default:
panic("unknown Kind")
}
return nil
}
Type Walk
package main
import (
"fmt"
"reflect"
"strings"
)
func main() {
var x struct {
Foo string
Bar struct {
Baz string
}
Bong [3]int
Biff *string
Bork map[int]int
Ch chan string
Sl []string
F func(x, y, z int) (w, t, e error)
}
typeWalk(x)
}
func typeWalk(val interface{}) error {
return rTypeWalk(reflect.ValueOf(val).Type(), 1)
}
func tabs(depth int) {
fmt.Print(strings.Repeat("\t", depth))
}
func rTypeWalk(rVal reflect.Type, depth int) error {
switch rVal.Kind() {
case reflect.Invalid:
fmt.Print("invalid")
case reflect.Bool:
fmt.Print("bool")
case reflect.Int:
fmt.Print("int")
case reflect.Int8:
fmt.Print("int8")
case reflect.Int16:
fmt.Print("int16")
case reflect.Int32:
fmt.Print("int32")
case reflect.Int64:
fmt.Print("int64")
case reflect.Uint:
fmt.Print("uint")
case reflect.Uint8:
fmt.Print("uint8")
case reflect.Uint16:
fmt.Print("uint16")
case reflect.Uint32:
fmt.Print("uint32")
case reflect.Uint64:
fmt.Print("uint64")
case reflect.Uintptr:
fmt.Print("uintptr")
case reflect.Float32:
fmt.Print("float32")
case reflect.Float64:
fmt.Print("float64")
case reflect.Complex64:
fmt.Print("complex64")
case reflect.Complex128:
fmt.Print("complex128")
case reflect.Array:
fmt.Print("[", rVal.Len(), "]")
rTypeWalk(rVal.Elem(), depth+1)
case reflect.Chan:
fmt.Print("chan ")
rTypeWalk(rVal.Elem(), depth+1)
case reflect.Func:
fmt.Print("func (")
for i := 0; i < rVal.NumIn(); i++ {
rTypeWalk(rVal.In(i), depth+1)
if i != rVal.NumIn()-1 {
fmt.Print(", ")
}
}
fmt.Print(") (")
for i := 0; i < rVal.NumOut(); i++ {
rTypeWalk(rVal.Out(i), depth+1)
if i != rVal.NumOut()-1 {
fmt.Print(", ")
}
}
fmt.Print(")")
case reflect.Interface:
fmt.Print("interface {")
for i := 0; i < rVal.NumMethod(); i++ {
m := rVal.Method(i)
fmt.Print(m.Name, " ")
rTypeWalk(m.Type, depth+1)
}
fmt.Print("}")
case reflect.Map:
fmt.Print("map[")
rTypeWalk(rVal.Key(), depth+1)
fmt.Print("]")
rTypeWalk(rVal.Elem(), depth+1)
case reflect.Ptr:
fmt.Print("*")
rTypeWalk(rVal.Elem(), depth+1)
case reflect.Slice:
fmt.Print("[]")
rTypeWalk(rVal.Elem(), depth+1)
case reflect.String:
fmt.Print("string")
case reflect.Struct:
fmt.Println("struct {")
fieldCount := rVal.NumField()
for i := 0; i < fieldCount; i++ {
f := rVal.Field(i)
tabs(depth)
fmt.Print(f.Name, " ")
rTypeWalk(f.Type, depth+1)
fmt.Println(f.Tag)
}
tabs(depth - 1)
fmt.Print("}")
case reflect.UnsafePointer:
fmt.Print("unsafe.Pointer")
default:
panic("unknown Kind")
}
return nil
}
rust + go
package main
// int rfg_int();
// #cgo LDFLAGS: -Loutput/ -lrfg
import "C"
import "fmt"
func main() {
fmt.Println("The next line is a number from rfg_int.rs:")
fmt.Println(C.rfg_int())
}
#[no_mangle]
pub extern "C" fn rfg_int() -> i32 {
42
}
OUTPUT=output
${OUTPUT}/rust_from_go: ${OUTPUT} ${OUTPUT}/librfg.a main.go
go build -o ${OUTPUT}/rust_from_go
${OUTPUT}/librfg.a: rfg_int.rs
rustc --crate-type staticlib -o ${OUTPUT}/librfg.a rfg_int.rs
${OUTPUT}:
-mkdir ${OUTPUT}
clean:
-rm -fr ${OUTPUT}
run: ${OUTPUT}/rust_from_go
${OUTPUT}/rust_from_go
Consuming a Trace
go tool trace <path-to-trace>
Build stuff
- Testing Flags
go doc cmd/link
:-X
go doc cmd/compile
: Compiler directives,-race
go doc cmd/go
shows (nearly?) all go flags
Links
- gophercon2018 performance tuning workshop
- Ultimate Go
- dvyukov/go-fuzz: Randomized testing for Go
- go-fuzz github.com/arolek/ase – Damian Gryski – Medium
- An Overview of Go's Tooling - Alex Edwards
- issre19_go.pdf
- go-sanitizer/checker.go at master · wangcong15/go-sanitizer
- goassert/goassert.go at master · wangcong15/goassert
- Using SO_PEERCRED in Go · jbowen.dev
- goccy/go-reflect: Zero-allocation reflection library for Go
- dave/dst: Decorated Syntax Tree - manipulate Go source with perfect fidelity.
- STAMP-project/pitest-descartes: Descartes supports developers to improve their test suites by reporting weak spots in covered code
Incubating Links to Read
- The Behavior Of Channels
- Code Review Comments
- conprof/conprof: Continuous profiling in for pprof compatible profiles.
- cron/parser.go at master · robfig/cron
- dgryski/go-perfbook: Thoughts on Go performance optimization
- Effective Go
- Extending Gotests for Strict Error Tests · JDHeyburn
- Functional programming in Go [case study] · YourBasic Go
- Garbage Collection In Go : Part I - Semantics
- Go 1.13: xerrors
- Go Best Practices 2016
- golangci-lint/new-linters.mdx at master · golangci/golangci-lint
- go-perfbook/performance.md at master · dgryski/go-perfbook
- GopherCon 2016: Francesc Campoy - Understanding nil - YouTube
- GopherCon 2017: Mitchell Hashimoto - Advanced Testing with Go - YouTube
- GopherCon 2019: Chris Hines - Death by 3,000 Timers: Streaming Video-on-Demand for Cable TV - YouTube X
- GopherCon 2019: Daniel Marti - Optimizing Go Code Without a Blindfold - YouTube O
- GopherCon 2019: Dave Cheney - Two Go Programs, Three Different Profiling Techniques - YouTube X
- GopherCon 2019: Jason Keene - Dynamically Instrumenting Go Programs - YouTube O
- GopherCon 2019: Jonathan Amsterdam - Detecting Incompatible API Changes - YouTube X
- GopherCon Russia 2019
- gopherdata/gophernotes: The Go kernel for Jupyter notebooks and nteract.
- go/README.md at master · golang/go
- Go’s hidden #pragmas
- Home - Practical Go Lessons Book
- How the Go runtime implements maps efficiently (without generics)
- How to Efficiently Compare Strings in Go
- Idiomatic Go Resources – Damian Gryski – Medium
- Meet the Authors – Go Language (Cloud Next '19) - YouTube
- mkevac/gopherconrussia2019: Accompanying code for Gophercon Russia 2019 talk about Bitmap Indexes
- Package names
- SOLID Go Design
- spilld/msgbuilder_test.go at master · spilled-ink/spilld
- staticcheck.io - Go static analysis
- Stop writing broken Go libraries: Do not use http.DefaultClient, do not use package vars, do not return interfaces
- Style guideline for Go packages
- Why Go? – Key advantages you may have overlooked · YourBasic Go
- Writing Useful go/analysis Linter · Denis Isaev Blog
- Processing Large Files – Java, Go and 'hitting the wall' | Ben E. C. Boyter
- HFO4/gameboy.live: 🕹️ A basic gameboy emulator with terminal "Cloud Gaming" support
- Who Needs Inheritance Anyway? – Shipt Tech
- Living without the modern browser – AN3223's Blog – Linux and stuff
- WebAssembly Troubles part 1: WebAssembly Is Not a Stack Machine
- Understanding real-world concurrency bugs in Go | the morning paper
- Test Go with interfaces, no Go type for types, an improved Go playground & more
- Optimizing M3: How Uber Halved Our Metrics Ingestion Latency by (Briefly) Forking the Go Compiler | Uber Engineering Blog
- Owning a language: C++ vs Go, simulate CPU in Golang & a Go + WASM tic-tac-toe bot
- cmd/go: build: add -static flag · Issue #26492 · golang/go
- cmd/compile: rewrite escape analysis · Issue #23109 · golang/go
- cmd/go: go build should be able to write multiple executables · Issue #14295 · golang/go
- proposal: testing: delay registration of flags until MainStart; add Init for explicit registration · Issue #21051 · golang/go
- cmd/compile: evaluate map initializers incrementally · Issue #26552 · golang/go
- encoding/json: speed up the decoding scanner · Issue #28923 · golang/go
- Go memory management, TCP keepalive in Go, adopting gRPC & more
- Dissecting Go Binaries
- The ultimate guide to writing a Go tool · Fatih Arslan
- Using go/analysis to write a custom linter · Fatih Arslan
- analysis - GoDoc
- delve/getting_started.md at master · go-delve/delve
- google/starlark-go: Starlark in Go: the Starlark configuration language, implemented in Go
- dgryski/go-interp: Interpolation search
- 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs
- Efficient Go APIs with the mid-stack inliner
- ORMs and Query Building in Go - andrew pillar
- To ORM or not to ORM - Eli Bendersky's website
- Self-referential functions and the design of options
- query/query_test.go at master · andrewpillar/query
- Michael McLoughlin on Twitter: "It was a privilege to speak at Gophercon yesterday about assembly generation in Go. https://t.co/YQdbQ1JpMu" / Twitter
- Dan Ballard 🌹🏴 on Twitter: "One of the best things I did was make our integration tests count goroutines throughout execution and fail if they weren't all cleaned up It's also one of the most annoying things ;) Tracking down a thread leak before I can submit this PR around app client/service reload" / Twitter
- felixge/fgprof: 🚀 fgprof is a sampling Go profiler that allows you to analyze On-CPU as well as Off-CPU (e.g. I/O) time together.
- dgryski/go-perfbook: Thoughts on Go performance optimization
- cockroachdb/errors: Go error library with error portability over the network
- GopherCon 2020 - Error handling and pitfalls in distributed Go apps - Google Slides
- tea package · pkg.go.dev