Skip to content

Commit

Permalink
initial commit of parser and goat-check utility
Browse files Browse the repository at this point in the history
  • Loading branch information
mknyszek committed May 6, 2020
0 parents commit 845c884
Show file tree
Hide file tree
Showing 7 changed files with 907 additions and 0 deletions.
157 changes: 157 additions & 0 deletions cmd/goat-check/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package main

import (
"errors"
"flag"
"fmt"
"io"
"os"
"sync"
"time"

"github.com/mknyszek/goat"
"github.com/mknyszek/goat/simulation/toolbox"

"golang.org/x/exp/mmap"
)

var printFlag *bool = flag.Bool("print", false, "print events as they're seen")

func init() {
flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
fmt.Fprintf(flag.CommandLine.Output(), "Utility that sanity-checks Go allocation traces\n")
fmt.Fprintf(flag.CommandLine.Output(), "and prints some statistics.\n")
fmt.Fprintf(flag.CommandLine.Output(), "usage: %s [flags] <allocation-trace-file>\n")
flag.PrintDefaults()
}
}

func handleError(err error, usage bool) {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
if usage {
flag.Usage()
}
os.Exit(1)
}

func main() {
flag.Parse()
if flag.NArg() != 1 {
handleError(errors.New("incorrect number of arguments"), true)
}
r, err := mmap.Open(flag.Arg(0))
if err != nil {
handleError(fmt.Errorf("incorrect number of arguments: %v", err), false)
}
defer r.Close()
fmt.Println("Generating parser...")
p, err := goat.NewParser(r)
if err != nil {
handleError(fmt.Errorf("creating parser: %v", err), false)
}
fmt.Println("Parsing events...")

var pMu sync.Mutex
progressDone := make(chan struct{})
go func() {
for {
pMu.Lock()
prog := p.Progress() * 100.0
pMu.Unlock()

fmt.Printf("Processing... %.4f%%\r", prog)
select {
case <-progressDone:
fmt.Println()
close(progressDone)
return
case <-time.After(time.Second):
}
}
}()

const maxErrors = 20
allocs, frees, gcs := 0, 0, 0
var sanity toolbox.AddressSet
var reuseWithoutFree []goat.Event
var doubleFree []goat.Event
minTicks := ^uint64(0)
for {
pMu.Lock()
ev, err := p.Next()
pMu.Unlock()
if err == io.EOF {
break
}
if minTicks == ^uint64(0) {
minTicks = ev.Timestamp
}
if err != nil {
handleError(fmt.Errorf("parsing events: %v", err), false)
}
switch ev.Kind {
case goat.EventAlloc:
if *printFlag {
fmt.Printf("[%d P %d] alloc(%d) @ 0x%x\n", ev.Timestamp-minTicks, ev.P, ev.Size, ev.Address)
}
if ok := sanity.Add(ev.Address); !ok {
reuseWithoutFree = append(reuseWithoutFree, ev)
}
allocs++
case goat.EventFree:
if *printFlag {
fmt.Printf("[%d P %d] free @ 0x%x\n", ev.Timestamp-minTicks, ev.P, ev.Address)
}
if ok := sanity.Remove(ev.Address); !ok {
doubleFree = append(doubleFree, ev)
}
frees++
case goat.EventGCEnd:
if *printFlag {
fmt.Printf("[%d P %d] GC end\n", ev.Timestamp-minTicks, ev.P)
}
gcs++
}
if len(reuseWithoutFree)+len(doubleFree) > maxErrors {
break
}
}
progressDone <- struct{}{}
<-progressDone

if errcount := len(reuseWithoutFree) + len(doubleFree); errcount != 0 {
tooMany := errcount > maxErrors
if tooMany {
errcount = maxErrors
fmt.Fprintf(os.Stderr, "found >%d errors in trace:\n", maxErrors)
} else {
fmt.Fprintf(os.Stderr, "found %d errors in trace:\n", errcount)
}
for i := 0; i < errcount; i++ {
ts1, ts2 := ^uint64(0), ^uint64(0)
var e1, e2 *goat.Event
if len(reuseWithoutFree) != 0 {
ts1 = reuseWithoutFree[0].Timestamp
e1 = &reuseWithoutFree[0]
}
if len(doubleFree) != 0 {
ts2 = doubleFree[0].Timestamp
e2 = &doubleFree[0]
}
if ts1 < ts2 {
fmt.Fprintf(os.Stderr, " allocated over slot 0x%x\n", e1.Address)
reuseWithoutFree = reuseWithoutFree[1:]
} else {
fmt.Fprintf(os.Stderr, " freed free slot 0x%x\n", e2.Address)
doubleFree = doubleFree[1:]
}
}
if tooMany {
fmt.Fprintf(os.Stderr, "too many errors\n")
}
}
fmt.Printf("Allocs: %d\n", allocs)
fmt.Printf("Frees: %d\n", frees)
fmt.Printf("GCs: %d\n", gcs)
}
39 changes: 39 additions & 0 deletions event.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package goat

// EventKind indicates what kind of allocation trace event
// is captured and returned.
type EventKind uint8

const (
EventBad EventKind = iota
EventAlloc // Allocation.
EventFree // Free.
EventGCEnd // GC mark termination.
)

// Event represents a single allocation trace event.
type Event struct {
// Timestamp is the time in non-normalized CPU ticks
// for this event.
Timestamp uint64

// Address is the address for the allocation or free.
// Only valid when Kind == EventAlloc or Kind == EventFree.
Address uint64

// Size indicates the size of the allocation.
// Only valid when Kind == EventAlloc.
Size uint64

// P indicates which processor generated the event.
// Valid for all events.
P int32

// Array indicates whether an allocation was for
// an array type.
Array bool

// Kind indicates what kind of event this is.
// This may be assumed to always be valid.
Kind EventKind
}
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/mknyszek/goat

go 1.14

require (
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a
)
25 changes: 25 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5 h1:FR+oGxGfbQu1d+jglI3rCkjAjUnhRSZcUxr+DqlDLNo=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Loading

0 comments on commit 845c884

Please sign in to comment.