Skip to content

Instantly share code, notes, and snippets.

@gobwas
Created June 18, 2017 21:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gobwas/1d5e4ccb24ee9bfc56eca8ac5ad8efd7 to your computer and use it in GitHub Desktop.
Save gobwas/1d5e4ccb24ee9bfc56eca8ac5ad8efd7 to your computer and use it in GitHub Desktop.
Stack growth test
package main
import (
"encoding/binary"
"flag"
"fmt"
"log"
"os"
"reflect"
"runtime"
"runtime/pprof"
"unsafe"
)
var (
memprofile = flag.String("memprofile", "", "write memory profile to `file`")
memprofilerate = flag.Int("memprofilerate", 0, "memory profile rate")
cpuprofile = flag.String("cpuprofile", "", "write cpu profile `file`")
)
//go:linkname runtimeMoreStack runtime.morestack
func runtimeMoreStack()
func main() {
flag.Parse()
if *memprofilerate != 0 {
runtime.MemProfileRate = *memprofilerate
}
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
log.Fatal("could not create CPU profile: ", err)
}
if err := pprof.StartCPUProfile(f); err != nil {
log.Fatal("could not start CPU profile: ", err)
}
defer pprof.StopCPUProfile()
}
done := make(chan struct{})
runGrower(done)
<-done
if *memprofile != "" {
f, err := os.Create(*memprofile)
if err != nil {
log.Fatal("could not create memory profile: ", err)
}
runtime.GC() // get up-to-date statistics
if err := pprof.WriteHeapProfile(f); err != nil {
log.Fatal("could not write memory profile: ", err)
}
f.Close()
}
}
//func unsafer(done chan struct{}) {
// defer close(done)
// var arr [8]byte
// ptr := uintptr(unsafe.Pointer(&arr))
// hdr := reflect.SliceHeader{Data: ptr, Len: len(arr), Cap: len(arr)}
// bts := *(*[]byte)(unsafe.Pointer(&hdr))
// fmt.Println(bts)
//}
func runGrower(done chan struct{}) {
defer close(done)
for i := 0; i < 4; i++ {
g := grower{}
g.growSlice()
fmt.Println("grow after", g.stackSize)
}
}
type grower struct {
sp uintptr
stackSize int64
}
func (g *grower) checkStackGrowth(p uintptr) (diff int64) {
if g.sp != 0 {
diff = int64(g.sp) - int64(p)
if diff > 0 {
g.stackSize += diff
}
}
g.sp = p
return
}
func (g *grower) growSlice() uintptr {
var arr [128]byte
ptr := uintptr(unsafe.Pointer(&arr))
hdr := &reflect.SliceHeader{Data: ptr, Len: len(arr), Cap: len(arr)}
bts := *(*[]byte)(unsafe.Pointer(hdr))
nuevo := g.doGrowSlice(bts, ptr)
acthdr := *(*reflect.SliceHeader)(unsafe.Pointer(&bts))
fmt.Println("after", acthdr.Data, ptr)
return nuevo
}
func (g *grower) doGrowSlice(p []byte, original uintptr) uintptr {
var bts [512]byte
_ = len(bts)
// Check stack growth according to orig arg (the highest arg on stack
// frame).
g.checkStackGrowth(uintptr(unsafe.Pointer(&original)))
hdr := *(*reflect.SliceHeader)(unsafe.Pointer(&p))
if actual := hdr.Data; actual != original {
return actual
}
Counter(p).Inc()
return g.doGrowSlice(p, original)
}
func (g *grower) growUint64() uintptr {
var v uint64
return g.doGrowUint64(&v, uintptr(unsafe.Pointer(&v)))
}
func (g *grower) doGrowUint64(v *uint64, original uintptr) uintptr {
var bts [512]byte
_ = len(bts)
// Check stack growth according to orig arg (the highest arg on stack
// frame).
g.checkStackGrowth(uintptr(unsafe.Pointer(&original)))
if actual := uintptr(unsafe.Pointer(v)); actual != original {
return actual
}
*v++
return g.doGrowUint64(v, original)
}
type Counter []byte
func (c Counter) Inc() {
c.Set(c.Val() + 1)
}
func (c Counter) Val() uint64 {
return binary.LittleEndian.Uint64([]byte(c)[:8])
}
func (c Counter) Set(v uint64) {
binary.LittleEndian.PutUint64([]byte(c)[:8], v)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment