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

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

(playground)

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

(playground)

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

(thanks to jeff)

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

related

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

See Also