├── .gitignore ├── go.mod ├── README.md ├── Makefile ├── internal ├── symbols │ ├── testdata │ │ └── main.go │ ├── binary_test.go │ └── binary.go ├── series │ ├── collection3.go │ ├── collection.go │ └── series.go ├── prof │ └── main.go ├── allocfreetrace │ ├── reader.go │ └── event.go ├── packet │ ├── encoder.go │ └── decoder.go └── g │ ├── math.go │ └── color.go ├── testdata ├── leaking │ └── leaking.go └── graph │ └── graph.go ├── format.go ├── LICENSE ├── summary.go ├── main.go ├── attach └── init.go ├── server.go ├── view.go └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | *.log 3 | *.exe -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module loov.dev/allocview 2 | 3 | go 1.14 4 | 5 | require ( 6 | gioui.org v0.0.0-20220726132227-f7bc744a24bf 7 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 8 | ) 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AllocView 2 | 3 | AllocView is for visualizing Go program allocations in real-time. 4 | 5 | ## How to use 6 | 7 | Run the program with: 8 | 9 | ``` 10 | allocview 11 | ``` 12 | 13 | The program should `import "loov.dev/allocview/attach"` to attach the program. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | testdata-graph: 3 | go build -o ./graph.exe ./testdata/graph 4 | go build -o ./allocview.exe . 5 | ./allocview.exe ./graph.exe 6 | 7 | testdata-leaking: 8 | go build -o ./leaking.exe ./testdata/leaking 9 | go build -o ./allocview.exe . 10 | ./allocview.exe ./leaking.exe 11 | -------------------------------------------------------------------------------- /internal/symbols/testdata/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "loov.dev/allocview/attach" 8 | ) 9 | 10 | //go:noinline 11 | func main() { 12 | hello() 13 | } 14 | 15 | //go:noinline 16 | func hello() { 17 | world() 18 | } 19 | 20 | //go:noinline 21 | func world() { 22 | var pcs [10]uintptr 23 | n := runtime.Callers(1, pcs[:]) 24 | for _, pc := range pcs[:n] { 25 | fmt.Println(pc) 26 | } 27 | } 28 | 29 | func init() { 30 | name, addr := attach.Addr() 31 | fmt.Println(name) 32 | fmt.Println(addr) 33 | } 34 | -------------------------------------------------------------------------------- /testdata/leaking/leaking.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | 7 | _ "loov.dev/allocview/attach" 8 | ) 9 | 10 | func main() { 11 | total := 0 12 | var leak [][]byte 13 | for { 14 | alloc := 10000 + rand.Intn(1000)*10 15 | mem := make([]byte, alloc) 16 | leak = append(leak, mem) 17 | total += len(mem) 18 | 19 | if rand.Intn(10) > 8 { 20 | i := rand.Intn(len(leak)) 21 | p := leak[i] 22 | leak = append(leak[:i], leak[i+1:]...) 23 | total -= len(p) 24 | } 25 | 26 | jitter := 30 + rand.Intn(30) 27 | time.Sleep(time.Duration(jitter) * time.Millisecond) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /format.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func SizeToString(bytes int64) string { 6 | abs := bytes 7 | if abs < 0 { 8 | abs = -abs 9 | } 10 | 11 | switch { 12 | case abs < (1<<10)*2/3: 13 | return fmt.Sprintf("%dB", bytes) 14 | case abs < (1<<20)*2/3: 15 | return fmt.Sprintf("%0.2fKB", float64(bytes)/float64(1<<10)) 16 | case abs < (1<<30)*2/3: 17 | return fmt.Sprintf("%0.2fMB", float64(bytes)/float64(1<<20)) 18 | case abs < (1<<40)*2/3: 19 | return fmt.Sprintf("%0.2fGB", float64(bytes)/float64(1<<30)) 20 | case abs < (1<<50)*2/3: 21 | return fmt.Sprintf("%0.2fTB", float64(bytes)/float64(1<<40)) 22 | default: 23 | return fmt.Sprintf("%0.2fPB", float64(bytes)/float64(1<<50)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /testdata/graph/graph.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "runtime" 5 | "time" 6 | 7 | _ "loov.dev/allocview/attach" 8 | ) 9 | 10 | type Node struct { 11 | Data []byte 12 | Links []*Node 13 | } 14 | 15 | func N(size int) *Node { 16 | return &Node{Data: make([]byte, size)} 17 | } 18 | 19 | var a, b, c, d, e, x *Node 20 | 21 | func main() { 22 | a = N(1 << 20) 23 | b = N(2 << 20) 24 | c = N(3 << 20) 25 | d = N(4 << 20) 26 | e = N(5 << 20) 27 | 28 | a.Links = []*Node{b, c} 29 | b.Links = []*Node{d, e, c} 30 | c.Links = []*Node{e} 31 | e.Links = []*Node{a, b} 32 | 33 | for i := 0; i < 10; i++ { 34 | x = N(6 << 20) 35 | time.Sleep(1 * time.Second) 36 | } 37 | 38 | runtime.KeepAlive(a) 39 | runtime.KeepAlive(b) 40 | runtime.KeepAlive(c) 41 | runtime.KeepAlive(d) 42 | runtime.KeepAlive(e) 43 | runtime.KeepAlive(x) 44 | 45 | runtime.GC() 46 | } 47 | 48 | func check(err error) { 49 | if err != nil { 50 | panic(err) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /internal/series/collection3.go: -------------------------------------------------------------------------------- 1 | package series 2 | 3 | import "time" 4 | 5 | // Collection3 implements sample aggregation based on 3 stack frames. 6 | type Collection3 struct { 7 | Collection 8 | ByStack map[[3]uintptr]*Series 9 | } 10 | 11 | // NewCollection3 returns a new Collection3. 12 | func NewCollection3(start time.Time, sampleDuration time.Duration, sampleCount int) *Collection3 { 13 | return &Collection3{ 14 | Collection: *NewCollection(start, sampleDuration, sampleCount), 15 | ByStack: make(map[[3]uintptr]*Series), 16 | } 17 | } 18 | 19 | // UpdateSample updates the sample at specified index for the specific stack. 20 | func (coll *Collection3) UpdateSample(index SampleIndex, stack []uintptr, sample Sample) { 21 | var h [3]uintptr 22 | copy(h[:], stack) 23 | 24 | series, ok := coll.ByStack[h] 25 | if !ok { 26 | series = &Series{ 27 | Stack: h[:], 28 | Samples: make([]Sample, coll.SampleCount), 29 | } 30 | coll.ByStack[h] = series 31 | coll.List = append(coll.List, series) 32 | } 33 | 34 | series.UpdateSample(index, sample) 35 | } 36 | -------------------------------------------------------------------------------- /internal/prof/main.go: -------------------------------------------------------------------------------- 1 | package prof 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "runtime/pprof" 7 | ) 8 | 9 | type Config struct { 10 | Cpu string 11 | Mem string 12 | 13 | cpufile *os.File 14 | } 15 | 16 | func (conf *Config) Run() func() { 17 | conf.Start() 18 | return conf.Stop 19 | } 20 | 21 | func (conf *Config) Start() { 22 | if conf.Cpu != "" { 23 | f, err := os.Create(conf.Cpu) 24 | if err != nil { 25 | log.Fatal("could not create CPU profile: ", err) 26 | } 27 | conf.cpufile = f 28 | if err := pprof.StartCPUProfile(f); err != nil { 29 | log.Fatal("could not start CPU profile: ", err) 30 | } 31 | } 32 | } 33 | 34 | func (conf *Config) Stop() { 35 | if conf.Mem != "" { 36 | f, err := os.Create(conf.Mem) 37 | if err != nil { 38 | log.Fatal("could not create memory profile: ", err) 39 | } 40 | if err := pprof.WriteHeapProfile(f); err != nil { 41 | log.Fatal("could not write memory profile: ", err) 42 | } 43 | f.Close() 44 | } 45 | 46 | if conf.Cpu != "" { 47 | pprof.StopCPUProfile() 48 | conf.cpufile.Close() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/allocfreetrace/reader.go: -------------------------------------------------------------------------------- 1 | package allocfreetrace 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | ) 8 | 9 | type Reader struct { 10 | input io.Reader 11 | scanner *bufio.Scanner 12 | } 13 | 14 | func NewReader(input io.Reader) *Reader { 15 | scanner := bufio.NewScanner(input) 16 | 17 | buffer := make([]byte, 10<<20) 18 | scanner.Buffer(buffer, 1<<20) 19 | scanner.Split(splitStack) 20 | 21 | return &Reader{ 22 | input: input, 23 | scanner: scanner, 24 | } 25 | } 26 | 27 | func (reader *Reader) Read() (Event, error) { 28 | tryagain: 29 | if !reader.scanner.Scan() { 30 | return Event{}, io.EOF 31 | } 32 | 33 | blocktext := reader.scanner.Text() 34 | event, ok := ParseEvent(blocktext) 35 | if !ok { 36 | goto tryagain 37 | } 38 | 39 | return event, nil 40 | } 41 | 42 | func splitStack(data []byte, atEOF bool) (advance int, token []byte, err error) { 43 | if atEOF && len(data) == 0 { 44 | return 0, nil, nil 45 | } 46 | if i := bytes.Index(data, []byte{'\n', '\n'}); i >= 0 { 47 | return i + 2, data[:i], nil 48 | } 49 | if atEOF { 50 | return len(data), data, nil 51 | } 52 | return 0, nil, nil 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Egon Elbre 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /internal/packet/encoder.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import "encoding/binary" 4 | 5 | type Encoder struct { 6 | data []byte 7 | } 8 | 9 | func NewEncoder(size int) Encoder { 10 | enc := Encoder{make([]byte, 0, size)} 11 | enc.Reset() 12 | return enc 13 | } 14 | 15 | func (enc *Encoder) Reset() { 16 | enc.data = enc.data[:0] 17 | enc.Int32(0) 18 | } 19 | 20 | func (enc *Encoder) BytesLen() int { 21 | return len(enc.data) - 4 22 | } 23 | 24 | func (enc *Encoder) LengthAndBytes() []byte { 25 | binary.LittleEndian.PutUint32(enc.data, uint32(len(enc.data)-4)) 26 | return enc.data 27 | } 28 | 29 | func (enc *Encoder) Byte(v byte) { 30 | enc.data = append(enc.data, v) 31 | } 32 | 33 | func (enc *Encoder) Uint32(v uint32) { 34 | var data [4]byte 35 | binary.LittleEndian.PutUint32(data[:], v) 36 | enc.data = append(enc.data, data[:]...) 37 | } 38 | 39 | func (enc *Encoder) Uint64(v uint64) { 40 | var data [8]byte 41 | binary.LittleEndian.PutUint64(data[:], v) 42 | enc.data = append(enc.data, data[:]...) 43 | } 44 | 45 | func (enc *Encoder) String(v string) { 46 | enc.Uint32(uint32(len(v))) 47 | enc.data = append(enc.data, []byte(v)...) 48 | } 49 | 50 | func (enc *Encoder) Int32(v int32) { 51 | enc.Uint32(uint32(v)) 52 | } 53 | 54 | func (enc *Encoder) Int64(v int64) { 55 | enc.Uint64(uint64(v)) 56 | } 57 | 58 | func (enc *Encoder) Uintptr(v uintptr) { 59 | enc.Uint64(uint64(v)) 60 | } 61 | -------------------------------------------------------------------------------- /internal/series/collection.go: -------------------------------------------------------------------------------- 1 | package series 2 | 3 | import "time" 4 | 5 | // Collection implements sample aggregation. 6 | type Collection struct { 7 | Start time.Time 8 | 9 | SampleDuration time.Duration 10 | SampleCount int 11 | 12 | SampleHead int 13 | LastNow time.Time 14 | 15 | List []*Series 16 | } 17 | 18 | // NewCollection returns a generic collection. 19 | func NewCollection(start time.Time, sampleDuration time.Duration, sampleCount int) *Collection { 20 | return &Collection{ 21 | Start: start, 22 | SampleDuration: sampleDuration, 23 | SampleCount: sampleCount, 24 | LastNow: start, 25 | } 26 | } 27 | 28 | // UpdateToTime updates Collection to the specified time and 29 | // returns the sample index corresponding to that time. 30 | func (coll *Collection) UpdateToTime(now time.Time) SampleIndex { 31 | local := now.Sub(coll.Start) 32 | sampleTime := int(local / coll.SampleDuration) 33 | 34 | if now.Before(coll.LastNow) { 35 | panic("time travel is not possible at this moment in time") 36 | } 37 | coll.LastNow = now 38 | 39 | // clear any old samples that were skipped 40 | if coll.SampleHead != sampleTime { 41 | // TODO: optimize this loop 42 | for _, s := range coll.List { 43 | for t := coll.SampleHead + 1; t < sampleTime; t++ { 44 | s.Samples[t%len(s.Samples)] = Sample{} 45 | } 46 | } 47 | coll.SampleHead = sampleTime 48 | } 49 | 50 | return SampleIndex(sampleTime % coll.SampleCount) 51 | } 52 | -------------------------------------------------------------------------------- /internal/series/series.go: -------------------------------------------------------------------------------- 1 | package series 2 | 3 | // Series is a ring-buffer indexed by Ring. 4 | type Series struct { 5 | Stack []uintptr 6 | 7 | TotalAllocBytes int64 8 | TotalAllocObjects int64 9 | Samples []Sample 10 | } 11 | 12 | // SampleIndex indexes Samples slice in Series. 13 | type SampleIndex int 14 | 15 | func (series *Series) UpdateSample(index SampleIndex, sample Sample) { 16 | series.TotalAllocBytes += sample.AllocBytes - sample.FreeBytes 17 | series.TotalAllocObjects += sample.AllocObjects - sample.FreeObjects 18 | 19 | series.Samples[index].Add(sample) 20 | } 21 | 22 | // Sample is total allocated or freed in SampleDuration. 23 | type Sample struct { 24 | AllocBytes int64 25 | FreeBytes int64 26 | AllocObjects int64 27 | FreeObjects int64 28 | } 29 | 30 | // Add calculates the total. 31 | func (sample *Sample) Add(b Sample) { 32 | sample.AllocBytes += b.AllocBytes 33 | sample.FreeBytes += b.FreeBytes 34 | sample.AllocObjects += b.AllocObjects 35 | sample.FreeObjects += b.FreeObjects 36 | } 37 | 38 | // Max returns the largest sample values. 39 | func (series *Series) Max() (r Sample) { 40 | for _, sample := range series.Samples { 41 | r.AllocBytes = max(r.AllocBytes, sample.AllocBytes) 42 | r.FreeBytes = max(r.FreeBytes, sample.FreeBytes) 43 | r.AllocObjects = max(r.AllocObjects, sample.AllocObjects) 44 | r.FreeObjects = max(r.FreeObjects, sample.FreeObjects) 45 | } 46 | return r 47 | } 48 | 49 | func (series *Series) MaxSampleBytes() (r int64) { 50 | for _, sample := range series.Samples { 51 | r = max(r, sample.AllocBytes) 52 | r = max(r, sample.FreeBytes) 53 | } 54 | return r 55 | } 56 | 57 | func max(a, b int64) int64 { 58 | if a > b { 59 | return a 60 | } 61 | return b 62 | } 63 | -------------------------------------------------------------------------------- /internal/packet/decoder.go: -------------------------------------------------------------------------------- 1 | package packet 2 | 3 | import ( 4 | "encoding/binary" 5 | "io" 6 | ) 7 | 8 | func ReadLength(r io.Reader) (int, error) { 9 | var buf [4]byte 10 | _, err := io.ReadFull(r, buf[:]) 11 | if err != nil { 12 | return 0, err 13 | } 14 | 15 | v := binary.LittleEndian.Uint32(buf[:]) 16 | return int(v), nil 17 | } 18 | 19 | type Decoder struct { 20 | off int 21 | data []byte 22 | } 23 | 24 | func (dec *Decoder) Read(r io.Reader) error { 25 | var lengthBuffer [4]byte 26 | _, err := io.ReadFull(r, lengthBuffer[:]) 27 | if err != nil { 28 | return err 29 | } 30 | 31 | length := binary.LittleEndian.Uint32(lengthBuffer[:]) 32 | 33 | // TODO: avoid realloc 34 | dec.off = 0 35 | dec.data = make([]byte, length) 36 | _, err = io.ReadFull(r, dec.data[:]) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | return nil 42 | } 43 | 44 | func (dec *Decoder) Reset(data []byte) { 45 | dec.off = 0 46 | dec.data = data 47 | } 48 | 49 | func (dec *Decoder) Byte() byte { 50 | dec.off++ 51 | return dec.data[dec.off-1] 52 | } 53 | 54 | func (dec *Decoder) Uint32() uint32 { 55 | v := binary.LittleEndian.Uint32(dec.data[dec.off:]) 56 | dec.off += 4 57 | return v 58 | } 59 | 60 | func (dec *Decoder) Uint64() uint64 { 61 | v := binary.LittleEndian.Uint64(dec.data[dec.off:]) 62 | dec.off += 8 63 | return v 64 | } 65 | 66 | func (dec *Decoder) Int32() int32 { 67 | return int32(dec.Uint32()) 68 | } 69 | 70 | func (dec *Decoder) Int64() int64 { 71 | return int64(dec.Uint64()) 72 | } 73 | 74 | func (dec *Decoder) Uintptr() uintptr { 75 | return uintptr(dec.Uint64()) 76 | } 77 | 78 | func (dec *Decoder) String() string { 79 | n := int(dec.Uint32()) 80 | b := dec.data[dec.off : dec.off+n] 81 | dec.off += n 82 | return string(b) 83 | } 84 | -------------------------------------------------------------------------------- /internal/symbols/binary_test.go: -------------------------------------------------------------------------------- 1 | package symbols_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "strconv" 9 | "strings" 10 | "testing" 11 | 12 | "loov.dev/allocview/internal/symbols" 13 | ) 14 | 15 | func TestSymbols(t *testing.T) { 16 | tempdir, err := ioutil.TempDir("", "") 17 | if err != nil { 18 | t.Fatal(err) 19 | } 20 | defer os.RemoveAll(tempdir) 21 | 22 | binpath := filepath.Join(tempdir, "test.exe") 23 | 24 | build := exec.Command("go", "build", "-o", binpath, "./testdata") 25 | _, err = build.CombinedOutput() 26 | if err != nil { 27 | t.Fatal(err) 28 | } 29 | 30 | expectedLines := []int{23, 17, 12} 31 | 32 | out, err := exec.Command(binpath).CombinedOutput() 33 | if err != nil { 34 | t.Fatal(err) 35 | } 36 | 37 | // binary outputs things in this order: 38 | 39 | // 40 | // 41 | // ... 42 | 43 | lines := strings.Split(string(out), "\n") 44 | symbolName := strings.TrimSpace(lines[0]) 45 | symbolAddr, err := strconv.ParseUint(lines[1], 10, 64) 46 | if err != nil { 47 | t.Fatal(err) 48 | } 49 | 50 | callers := []uint64{} 51 | for _, frame := range lines[2:] { 52 | frame = strings.TrimSpace(frame) 53 | if frame == "" { 54 | continue 55 | } 56 | pc, err := strconv.ParseUint(frame, 10, 64) 57 | if err != nil { 58 | t.Fatal(err) 59 | } 60 | callers = append(callers, pc) 61 | } 62 | if len(callers) < 3 { 63 | t.Fatal("not enough callers") 64 | } 65 | 66 | bin, err := symbols.Load(binpath) 67 | if err != nil { 68 | t.Fatal(err) 69 | } 70 | 71 | sym := bin.SymTable.LookupFunc(symbolName) 72 | symbolOffset := sym.Entry - symbolAddr 73 | 74 | for i, expline := range expectedLines { 75 | pc := callers[i] 76 | _, line, _ := bin.SymTable.PCToLine(pc + symbolOffset - 1) 77 | if expline != line { 78 | t.Errorf("got line %d, expected %d", line, expline) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /summary.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "log" 7 | "time" 8 | 9 | "loov.dev/allocview/internal/series" 10 | "loov.dev/allocview/internal/symbols" 11 | ) 12 | 13 | type Summary struct { 14 | Config Config 15 | 16 | Symbols *symbols.Binary 17 | Collection *series.Collection3 18 | } 19 | 20 | func NewSummary(config Config) *Summary { 21 | return &Summary{ 22 | Config: config, 23 | Collection: series.NewCollection3(time.Now(), config.SampleDuration, config.SampleCount), 24 | } 25 | } 26 | 27 | // Add adds profile to the collections. 28 | func (summary *Summary) Add(profile *Profile) { 29 | // TODO: per binary symbols 30 | if summary.Symbols == nil { 31 | // TODO: is there a better location to do this? 32 | var err error 33 | summary.Symbols, err = symbols.Load(profile.ExeName) 34 | if err != nil { 35 | log.Fatal(err) 36 | } 37 | 38 | summary.Symbols.UpdateOffset(profile.FuncName, profile.FuncAddr) 39 | } 40 | 41 | collection := summary.Collection 42 | index := collection.UpdateToTime(profile.Time) 43 | for i := range profile.Records { 44 | rec := &profile.Records[i] 45 | for i, frame := range rec.Stack0 { 46 | if frame == 0 { 47 | break 48 | } 49 | rec.Stack0[i] = uintptr(int64(frame) + summary.Symbols.Offset) 50 | } 51 | 52 | // TODO: implement skip runtime 53 | collection.UpdateSample(index, rec.Stack0[:], series.Sample{ 54 | AllocBytes: rec.AllocBytes, 55 | FreeBytes: rec.FreeBytes, 56 | AllocObjects: rec.AllocObjects, 57 | FreeObjects: rec.FreeObjects, 58 | }) 59 | } 60 | 61 | // TODO: reuse profile allocation 62 | } 63 | 64 | func (summary *Summary) StackAsString(stack []uintptr) string { 65 | var s bytes.Buffer 66 | for _, frame := range stack { 67 | if frame == 0 { 68 | break 69 | } 70 | 71 | file, line, _ := summary.Symbols.SymTable.PCToLine(uint64(frame)) 72 | if file == "" { 73 | fmt.Fprintf(&s, "0x%x\n", frame) 74 | continue 75 | } 76 | fmt.Fprintf(&s, "%s:%v\n", file, line) 77 | } 78 | return s.String() 79 | } 80 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "runtime" 11 | "time" 12 | 13 | "gioui.org/app" 14 | "gioui.org/unit" 15 | "golang.org/x/sync/errgroup" 16 | 17 | "loov.dev/allocview/internal/prof" 18 | ) 19 | 20 | func init() { 21 | runtime.LockOSThread() 22 | } 23 | 24 | func main() { 25 | ctx := context.Background() 26 | 27 | flag.Usage = func() { 28 | w := flag.CommandLine.Output() 29 | fmt.Fprintf(w, `Usage: %s [flags] subcommand... 30 | 31 | This tool visualizes allocations of a Go program. 32 | 33 | Only programs that have imported "loov.dev/allocview/attach" are supported at the moment. 34 | 35 | When given a subcommand, it executes that subcommand and starts live-visualization 36 | of the program. As an example: 37 | 38 | allocview go run ./testdata 39 | 40 | Flags: 41 | `, os.Args[0]) 42 | flag.PrintDefaults() 43 | } 44 | 45 | var profcfg prof.Config 46 | flag.StringVar(&profcfg.Cpu, "cpuprofile", "", "write cpu profile to `file`") 47 | flag.StringVar(&profcfg.Mem, "memprofile", "", "write memory profile to `file`") 48 | 49 | var config Config 50 | 51 | flag.DurationVar(&config.SampleDuration, "sample-duration", time.Second, "sample duration") 52 | flag.IntVar(&config.SampleCount, "sample-count", 1024, "sample count") 53 | 54 | flag.Parse() 55 | 56 | if len(flag.Args()) == 0 { 57 | flag.Usage() 58 | os.Exit(2) 59 | } 60 | 61 | defer profcfg.Run()() 62 | 63 | // Setup command that we want to monitor. 64 | args := flag.Args() 65 | cmd := exec.Command(args[0], args[1:]...) 66 | cmd.Stdin = os.Stdin 67 | cmd.Stdout = os.Stdout 68 | cmd.Stderr = os.Stderr 69 | 70 | var group errgroup.Group 71 | 72 | server := NewServer() 73 | err := server.Exec(ctx, &group, cmd) 74 | if err != nil { 75 | log.Fatal(err) 76 | } 77 | 78 | group.Go(func() error { 79 | window := app.NewWindow( 80 | app.Size(unit.Dp(800), unit.Dp(650)), 81 | app.Title("AllocView"), 82 | ) 83 | view := NewView(config, server) 84 | return view.Run(window) 85 | }) 86 | 87 | app.Main() 88 | 89 | err = group.Wait() 90 | if err != nil { 91 | log.Println(err) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /internal/g/math.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import "math" 4 | 5 | const ( 6 | Pi = math.Pi 7 | Tau = 2 * math.Pi 8 | Sqrt2 = math.Sqrt2 9 | ) 10 | 11 | func Pow(base, e float32) float32 { return float32(math.Pow(float64(base), float64(e))) } 12 | func Mod(x, y float32) float32 { return float32(math.Mod(float64(x), float64(y))) } 13 | func Sqr(v float32) float32 { return v * v } 14 | func Sqrt(v float32) float32 { return float32(math.Sqrt(float64(v))) } 15 | 16 | func Ceil(v float32) float32 { return float32(math.Ceil(float64(v))) } 17 | func Floor(v float32) float32 { return float32(math.Floor(float64(v))) } 18 | 19 | func Sin(v float32) float32 { return float32(math.Sin(float64(v))) } 20 | func Cos(v float32) float32 { return float32(math.Cos(float64(v))) } 21 | 22 | func Sincos(v float32) (float32, float32) { 23 | sn, cs := math.Sincos(float64(v)) 24 | return float32(sn), float32(cs) 25 | } 26 | 27 | func Abs(v float32) float32 { 28 | if v < 0 { 29 | return -v 30 | } 31 | return v 32 | } 33 | 34 | func Min(a, b float32) float32 { 35 | if a <= b { 36 | return a 37 | } 38 | return b 39 | } 40 | 41 | func Max(a, b float32) float32 { 42 | if a >= b { 43 | return a 44 | } 45 | return b 46 | } 47 | 48 | func MinMax(a, b float32) (float32, float32) { 49 | if a < b { 50 | return a, b 51 | } 52 | return b, a 53 | } 54 | 55 | func Lerp(p, min, max float32) float32 { 56 | return min + (max-min)*p 57 | } 58 | 59 | func InverseLerp(p, min, max float32) float32 { 60 | return (p - min) / (max - min) 61 | } 62 | 63 | func LerpClamp(p, min, max float32) float32 { 64 | if p < 0 { 65 | return min 66 | } else if p > 1 { 67 | return max 68 | } 69 | return min + (max-min)*p 70 | } 71 | 72 | func InverseLerpClamp(p, min, max float32) float32 { 73 | if p < min { 74 | return min 75 | } else if p > max { 76 | return max 77 | } 78 | return (p - min) / (max - min) 79 | } 80 | 81 | func Clamp(v, min, max float32) float32 { 82 | if v < min { 83 | return min 84 | } else if v > max { 85 | return max 86 | } 87 | return v 88 | } 89 | 90 | func Clamp01(v float32) float32 { 91 | if v < 0 { 92 | return 0 93 | } else if v > 1 { 94 | return 1 95 | } 96 | return v 97 | } 98 | 99 | func Clamp1(v float32) float32 { 100 | if v < -1 { 101 | return -1 102 | } else if v > 1 { 103 | return 1 104 | } 105 | return v 106 | } 107 | -------------------------------------------------------------------------------- /attach/init.go: -------------------------------------------------------------------------------- 1 | package attach 2 | 3 | import ( 4 | "net" 5 | "os" 6 | "reflect" 7 | "runtime" 8 | "time" 9 | 10 | "loov.dev/allocview/internal/packet" 11 | ) 12 | 13 | // Addr returns the address of this func. 14 | // go:noinline 15 | func Addr() (string, uintptr) { 16 | addr := reflect.ValueOf(Addr).Pointer() 17 | fn := runtime.FuncForPC(addr) 18 | return fn.Name(), addr 19 | } 20 | 21 | func init() { 22 | sockPath := os.Getenv("ALLOCLOGSOCK") 23 | if sockPath == "" { 24 | return 25 | } 26 | 27 | exe, err := os.Executable() 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | sockAddr, err := net.ResolveUnixAddr("unix", sockPath) 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | conn, err := net.DialUnix("unix", nil, sockAddr) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | runtime.MemProfileRate = 1 43 | 44 | err = monitor(exe, conn) 45 | if err != nil { 46 | panic(err) 47 | } 48 | } 49 | 50 | func monitor(exe string, conn *net.UnixConn) error { 51 | enc := packet.NewEncoder(1 << 20) 52 | 53 | enc.String("alloclog") 54 | enc.String(exe) 55 | 56 | name, addr := Addr() 57 | enc.String(name) 58 | enc.Uintptr(addr) 59 | 60 | if _, err := conn.Write(enc.LengthAndBytes()); err != nil { 61 | _ = conn.Close() 62 | return err 63 | } 64 | 65 | go func() { 66 | defer conn.Close() 67 | 68 | tick := time.NewTicker(time.Second / 10) 69 | records := make([]runtime.MemProfileRecord, 1000) 70 | for t := range tick.C { 71 | // TODO: figure out a better way to do this 72 | // runtime.GC forces mem profile to be updated 73 | runtime.GC() 74 | tryagain: 75 | n, ok := runtime.MemProfile(records, true) 76 | if !ok { 77 | records = make([]runtime.MemProfileRecord, n+n/3) 78 | goto tryagain 79 | } 80 | enc.Reset() 81 | 82 | enc.Int64(t.UnixNano()) 83 | 84 | enc.Uint32(uint32(n)) 85 | nextRecord: 86 | for _, rec := range records[:n] { 87 | enc.Int64(rec.AllocBytes) 88 | enc.Int64(rec.FreeBytes) 89 | enc.Int64(rec.AllocObjects) 90 | enc.Int64(rec.FreeObjects) 91 | 92 | for _, frame := range rec.Stack0 { 93 | enc.Uintptr(frame) 94 | if frame == 0 { 95 | continue nextRecord 96 | } 97 | } 98 | enc.Uintptr(0) 99 | } 100 | 101 | if _, err := conn.Write(enc.LengthAndBytes()); err != nil { 102 | panic(err) 103 | } 104 | } 105 | }() 106 | 107 | return nil 108 | } 109 | -------------------------------------------------------------------------------- /internal/allocfreetrace/event.go: -------------------------------------------------------------------------------- 1 | package allocfreetrace 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | type Kind byte 11 | 12 | const ( 13 | Invalid Kind = iota 14 | Alloc 15 | Free 16 | GC 17 | ) 18 | 19 | func (kind Kind) String() string { 20 | switch kind { 21 | case Alloc: 22 | return "alloc" 23 | case Free: 24 | return "free" 25 | case GC: 26 | return "gc" 27 | default: 28 | return "invalid" 29 | } 30 | } 31 | 32 | type Event struct { 33 | Kind Kind 34 | Address Address 35 | Type string 36 | Size int64 37 | Stack string 38 | } 39 | 40 | type Address uintptr 41 | 42 | func ParseEvent(block string) (Event, bool) { 43 | header, stack := splitBlock(block) 44 | kind, address, typ, size := parseHeader(header) 45 | if kind == Invalid || kind == GC { 46 | return Event{}, false 47 | } 48 | 49 | return Event{ 50 | Kind: kind, 51 | Address: address, 52 | Type: typ, 53 | Size: size, 54 | Stack: stack, 55 | }, true 56 | } 57 | 58 | var ( 59 | rxAlloc = regexp.MustCompile(`^\(0x([0-9a-f]+), 0x([0-9a-f]+)(?:, (.*))?\)$`) 60 | rxFree = regexp.MustCompile(`^\(0x([0-9a-f]+), 0x([0-9a-f]+)\)$`) 61 | ) 62 | 63 | func parseHeader(header string) (Kind, Address, string, int64) { 64 | p := strings.IndexAny(header, "( ") 65 | if p < 0 { 66 | return Invalid, 0, "", 0 67 | } 68 | 69 | switch header[:p] { 70 | case "tracealloc": 71 | // tracealloc(0xc00005ea80, 0x180, runtime.g) 72 | // tracealloc(0xc00005ea80, 0x180) 73 | tokens := rxAlloc.FindStringSubmatch(header[p:]) 74 | if len(tokens) != 4 { 75 | fmt.Printf("%q %v\n", header[p:], tokens) 76 | panic(header) 77 | } 78 | address, err := strconv.ParseUint(tokens[1], 16, 64) 79 | if err != nil { 80 | panic(err) 81 | } 82 | size, err := strconv.ParseInt(tokens[2], 16, 64) 83 | if err != nil { 84 | panic(err) 85 | } 86 | return Alloc, Address(address), tokens[3], size 87 | case "tracefree": 88 | // tracefree(0xc0006a2090, 0x30) 89 | tokens := rxFree.FindStringSubmatch(header[p:]) 90 | if len(tokens) != 3 { 91 | fmt.Printf("%q\n", header[p:]) 92 | panic(header) 93 | } 94 | address, err := strconv.ParseUint(tokens[1], 16, 64) 95 | if err != nil { 96 | panic(err) 97 | } 98 | size, err := strconv.ParseInt(tokens[2], 16, 64) 99 | if err != nil { 100 | panic(err) 101 | } 102 | return Free, Address(address), "", size 103 | case "tracegc": 104 | return GC, 0, "", 0 105 | case "goroutine": 106 | return Invalid, 0, "", 0 107 | default: 108 | return Invalid, 0, "", 0 109 | } 110 | } 111 | 112 | func splitBlock(block string) (header, stack string) { 113 | tokens := strings.SplitN(block, "\n", 2) 114 | if len(tokens) != 2 { 115 | return block, "" 116 | } 117 | return tokens[0], tokens[1] 118 | } 119 | -------------------------------------------------------------------------------- /internal/g/color.go: -------------------------------------------------------------------------------- 1 | package g 2 | 3 | import "image/color" 4 | 5 | type Color = color.NRGBA 6 | 7 | var ( 8 | White = Color{0xFF, 0xFF, 0xFF, 0xFF} 9 | Black = Color{0x00, 0x00, 0x00, 0xFF} 10 | Red = Color{0xFF, 0x00, 0x00, 0xFF} 11 | Green = Color{0x00, 0xFF, 0x00, 0xFF} 12 | Blue = Color{0x00, 0x00, 0xFF, 0xFF} 13 | Yellow = Color{0xFF, 0xFF, 0x00, 0xFF} 14 | 15 | Transparent = Color{0xFF, 0xFF, 0xFF, 0x00} 16 | ) 17 | 18 | func RGBAHex(hex uint32) Color { 19 | return Color{ 20 | R: uint8(hex >> 24), 21 | G: uint8(hex >> 16), 22 | B: uint8(hex >> 8), 23 | A: uint8(hex >> 0), 24 | } 25 | } 26 | 27 | // RGB returns color based on RGB in range 0..1 28 | func RGB(r, g, b float32) Color { return Color{Sat8(r), Sat8(g), Sat8(b), 0xFF} } 29 | 30 | // RGBA returns color based on RGBA in range 0..1 31 | func RGBA(r, g, b, a float32) Color { return Color{Sat8(r), Sat8(g), Sat8(b), Sat8(a)} } 32 | 33 | // HSLA returns color based on HSLA in range 0..1 34 | func HSLA(h, s, l, a float32) Color { return RGBA(hsla(h, s, l, a)) } 35 | 36 | // HSL returns color based on HSL in range 0..1 37 | func HSL(h, s, l float32) Color { return HSLA(h, s, l, 1) } 38 | 39 | // ColorWithAlpha returns new color with different alpha 40 | func ColorWithAlpha(c Color, a uint8) Color { 41 | c.A = a 42 | return c 43 | } 44 | 45 | // IsTransparent returns whether the color is fully transparent 46 | func IsTransparent(c Color) bool { 47 | return c.A == 0 48 | } 49 | 50 | // ColorAsFloat returns RGBA scaled to 0..1 51 | func ColorAsFloat(c Color) (r, g, b, a float32) { 52 | return float32(c.R) / 0xFF, float32(c.G) / 0xFF, float32(c.B) / 0xFF, float32(c.A) / 0xFF 53 | } 54 | 55 | // ColorBytes returns []byte{R, G, B, A} 56 | func ColorBytes(c Color) []byte { return []byte{c.R, c.G, c.B, c.A} } 57 | 58 | // Lerp linearly interpolates each RGBA component separately 59 | func ColorLerp(a, b Color, p float32) Color { 60 | ar, ag, ab, aa := ColorAsFloat(a) 61 | br, bg, bb, ba := ColorAsFloat(b) 62 | return RGBA( 63 | LerpClamp(ar, br, p), 64 | LerpClamp(ag, bg, p), 65 | LerpClamp(ab, bb, p), 66 | LerpClamp(aa, ba, p), 67 | ) 68 | } 69 | func hue(v1, v2, h float32) float32 { 70 | if h < 0 { 71 | h += 1 72 | } 73 | if h > 1 { 74 | h -= 1 75 | } 76 | if 6*h < 1 { 77 | return v1 + (v2-v1)*6*h 78 | } else if 2*h < 1 { 79 | return v2 80 | } else if 3*h < 2 { 81 | return v1 + (v2-v1)*(2.0/3.0-h)*6 82 | } 83 | 84 | return v1 85 | } 86 | 87 | func hsla(h, s, l, a float32) (r, g, b, ra float32) { 88 | if s == 0 { 89 | return l, l, l, a 90 | } 91 | 92 | h = Mod(h, 1) 93 | 94 | var v2 float32 95 | if l < 0.5 { 96 | v2 = l * (1 + s) 97 | } else { 98 | v2 = (l + s) - s*l 99 | } 100 | 101 | v1 := 2*l - v2 102 | r = hue(v1, v2, h+1.0/3.0) 103 | g = hue(v1, v2, h) 104 | b = hue(v1, v2, h-1.0/3.0) 105 | ra = a 106 | 107 | return 108 | } 109 | 110 | // Sat8 converts 0..1 float to 0..255 uint8 111 | func Sat8(v float32) uint8 { 112 | v *= 255.0 113 | if v >= 255 { 114 | return 255 115 | } else if v <= 0 { 116 | return 0 117 | } 118 | return uint8(v) 119 | } 120 | -------------------------------------------------------------------------------- /internal/symbols/binary.go: -------------------------------------------------------------------------------- 1 | package symbols 2 | 3 | import ( 4 | "debug/dwarf" 5 | "debug/elf" 6 | "debug/gosym" 7 | "debug/macho" 8 | "debug/pe" 9 | "fmt" 10 | ) 11 | 12 | // Binary handles symbol lookup based on stack frames. 13 | type Binary struct { 14 | Data *dwarf.Data 15 | 16 | SymTable *gosym.Table 17 | 18 | Offset int64 19 | } 20 | 21 | func Load(path string) (*Binary, error) { 22 | data, symtab, err := loadDwarfData(path) 23 | if err != nil { 24 | return nil, fmt.Errorf("unable to load dwarf data: %w", err) 25 | } 26 | 27 | return &Binary{ 28 | Data: data, 29 | 30 | SymTable: symtab, 31 | }, nil 32 | } 33 | 34 | func (bin *Binary) UpdateOffset(funcname string, funcaddr uintptr) { 35 | // TODO: handle errors 36 | sym := bin.SymTable.LookupFunc(funcname) 37 | if sym == nil { 38 | return 39 | } 40 | bin.Offset = int64(sym.Entry) - int64(funcaddr) 41 | } 42 | 43 | func loadDwarfData(path string) (*dwarf.Data, *gosym.Table, error) { 44 | { // try elf 45 | f, err := elf.Open(path) 46 | if err == nil { 47 | d, err := f.DWARF() 48 | if err != nil { 49 | return nil, nil, fmt.Errorf("elf %q: unable to read: %w", path, err) 50 | } 51 | 52 | symtab, err := elfLoadTable(f) 53 | if err != nil { 54 | return nil, nil, fmt.Errorf("pe %q: unable to read symtab: %w", path, err) 55 | } 56 | 57 | return d, symtab, nil 58 | } 59 | } 60 | 61 | { // try macho 62 | f, err := macho.Open(path) 63 | if err == nil { 64 | d, err := f.DWARF() 65 | if err != nil { 66 | return nil, nil, fmt.Errorf("macho %q: unable to read: %w", path, err) 67 | } 68 | 69 | symtab, err := machoLoadTable(f) 70 | if err != nil { 71 | return nil, nil, fmt.Errorf("pe %q: unable to read symtab: %w", path, err) 72 | } 73 | 74 | return d, symtab, nil 75 | } 76 | } 77 | 78 | { // try pe 79 | f, err := pe.Open(path) 80 | if err == nil { 81 | d, err := f.DWARF() 82 | if err != nil { 83 | return nil, nil, fmt.Errorf("pe %q: unable to read: %w", path, err) 84 | } 85 | 86 | symtab, err := peLoadTable(f) 87 | if err != nil { 88 | return nil, nil, fmt.Errorf("pe %q: unable to read symtab: %w", path, err) 89 | } 90 | 91 | return d, symtab, nil 92 | } 93 | } 94 | 95 | return nil, nil, fmt.Errorf("%q has unknown format", path) 96 | } 97 | 98 | func elfLoadTable(f *elf.File) (*gosym.Table, error) { 99 | section := f.Section(".gopclntab") 100 | if section == nil { 101 | return nil, fmt.Errorf("unable to find pclntab") 102 | } 103 | 104 | data, err := section.Data() 105 | if err != nil { 106 | return nil, fmt.Errorf("unable to read pclntab data: %w", err) 107 | } 108 | 109 | lineTable := gosym.NewLineTable(data, f.Section(".text").Addr) 110 | symTable, err := gosym.NewTable(nil, lineTable) 111 | if err != nil { 112 | return nil, fmt.Errorf("unable to create gosym table: %w", err) 113 | } 114 | return symTable, nil 115 | } 116 | 117 | func machoLoadTable(f *macho.File) (*gosym.Table, error) { 118 | section := f.Section("__gopclntab") 119 | if section == nil { 120 | return nil, fmt.Errorf("unable to find pclntab") 121 | } 122 | 123 | data, err := section.Data() 124 | if err != nil { 125 | return nil, fmt.Errorf("unable to read pclntab data: %w", err) 126 | } 127 | 128 | lineTable := gosym.NewLineTable(data, f.Section("__text").Addr) 129 | symTable, err := gosym.NewTable(nil, lineTable) 130 | if err != nil { 131 | return nil, fmt.Errorf("unable to create gosym table: %w", err) 132 | } 133 | return symTable, nil 134 | } 135 | 136 | func peLoadTable(f *pe.File) (*gosym.Table, error) { 137 | pclntab, err := peLoadRange(f, "runtime.pclntab", "runtime.epclntab") 138 | if err != nil { 139 | return nil, fmt.Errorf("unable to read pclntab: %w", err) 140 | } 141 | 142 | symtab, err := peLoadRange(f, "runtime.symtab", "runtime.esymtab") 143 | if err != nil { 144 | return nil, fmt.Errorf("unable to read symtab: %w", err) 145 | } 146 | 147 | lineTable := gosym.NewLineTable(pclntab, 0) 148 | symTable, err := gosym.NewTable(symtab, lineTable) 149 | if err != nil { 150 | return nil, fmt.Errorf("unable to create gosym table: %w", err) 151 | } 152 | return symTable, nil 153 | } 154 | 155 | func peLoadRange(f *pe.File, sname, ename string) ([]byte, error) { 156 | start := peFindSymbol(f, sname) 157 | if start == nil { 158 | return nil, fmt.Errorf("unable to find start symbol %q", sname) 159 | } 160 | 161 | end := peFindSymbol(f, ename) 162 | if end == nil { 163 | return nil, fmt.Errorf("unable to find end symbol %q", ename) 164 | } 165 | 166 | if start.SectionNumber != end.SectionNumber { 167 | return nil, fmt.Errorf("start %q and end %q section do not match", sname, ename) 168 | } 169 | 170 | sect := f.Sections[start.SectionNumber-1] 171 | data, err := sect.Data() 172 | if err != nil { 173 | return nil, fmt.Errorf("start %q and end %q section failed to load", sname, ename) 174 | } 175 | 176 | return data[start.Value:end.Value], nil 177 | } 178 | 179 | func peFindSymbol(f *pe.File, name string) *pe.Symbol { 180 | for _, sym := range f.Symbols { 181 | if sym.Name == name { 182 | return sym 183 | } 184 | } 185 | return nil 186 | } 187 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "net" 9 | "os" 10 | "os/exec" 11 | "runtime" 12 | "time" 13 | 14 | "golang.org/x/sync/errgroup" 15 | 16 | "loov.dev/allocview/internal/packet" 17 | "loov.dev/allocview/internal/series" 18 | ) 19 | 20 | // ConnectDeadline defines how fast clients should connect to the server. 21 | const ConnectDeadline = 10 * time.Second 22 | 23 | // Server is a profile listening server. 24 | type Server struct { 25 | profiles chan *Profile 26 | } 27 | 28 | // NewServer returns a new server. 29 | func NewServer() *Server { 30 | return &Server{ 31 | profiles: make(chan *Profile, 1024), 32 | } 33 | } 34 | 35 | // Profile returns channel for profiles. 36 | func (server *Server) Profiles() <-chan *Profile { return server.profiles } 37 | 38 | // Exec starts listening to cmd. 39 | func (server *Server) Exec(ctx context.Context, group *errgroup.Group, cmd *exec.Cmd) error { 40 | // create a temporary socket name 41 | tmpfile, err := ioutil.TempFile("", "alloclog") 42 | if err != nil { 43 | return fmt.Errorf("unable to create temporary alloclog file: %w", err) 44 | } 45 | sockname := tmpfile.Name() 46 | tmpfile.Close() 47 | os.Remove(sockname) 48 | 49 | // setup listener 50 | addr := &net.UnixAddr{Name: sockname, Net: "unix"} 51 | sock, err := net.ListenUnix("unix", addr) 52 | if err != nil { 53 | return fmt.Errorf("unable to start unix socket on %q: %w", sockname, err) 54 | } 55 | sock.SetUnlinkOnClose(true) 56 | 57 | // start the program 58 | cmd.Env = append( 59 | os.Environ(), 60 | "ALLOCLOGSOCK="+sockname, 61 | ) 62 | err = cmd.Start() // TODO: use pgroup 63 | if err != nil { 64 | return fmt.Errorf("failed to start %q: %w", cmd.Args, err) 65 | } 66 | 67 | // wait for the program to connect 68 | err = sock.SetDeadline(time.Now().Add(ConnectDeadline)) 69 | if err != nil { 70 | _ = cmd.Process.Kill() 71 | _ = sock.Close() 72 | return fmt.Errorf("failed to set socket deadline: %w", err) 73 | } 74 | 75 | conn, err := sock.AcceptUnix() 76 | if err != nil { 77 | _ = cmd.Process.Kill() 78 | _ = sock.Close() 79 | return fmt.Errorf("no connection established, did you import `loov.dev/allocview/attach`: %w", err) 80 | } 81 | 82 | // we'll set deadline for the first packet to handle misconfigurations 83 | err = conn.SetReadDeadline(time.Now().Add(ConnectDeadline)) 84 | if err != nil { 85 | _ = cmd.Process.Kill() 86 | _ = sock.Close() 87 | return fmt.Errorf("failed to set read deadline: %w", err) 88 | } 89 | var dec packet.Decoder 90 | err = dec.Read(conn) 91 | if err != nil { 92 | return fmt.Errorf("failed to read first packet: %w", err) 93 | } 94 | err = conn.SetReadDeadline(time.Time{}) 95 | if err != nil { 96 | _ = cmd.Process.Kill() 97 | _ = sock.Close() 98 | return fmt.Errorf("failed to set read deadline: %w", err) 99 | } 100 | 101 | // TODO: handle magic header better 102 | magic := dec.String() 103 | if magic != "alloclog" { 104 | return fmt.Errorf("invalid header %q expected %q", magic, "alloclog") 105 | } 106 | 107 | exename := dec.String() 108 | funcname := dec.String() 109 | funcaddr := dec.Uintptr() 110 | 111 | // Reading of profiles. 112 | group.Go(func() error { 113 | err := server.readProfiles(conn, exename, funcname, funcaddr) 114 | log.Printf("readProfiles returned: %v", err) 115 | return err 116 | }) 117 | 118 | group.Go(func() error { 119 | // waits for program to close 120 | err := cmd.Wait() 121 | log.Printf("program exited: %v", err) 122 | return err 123 | }) 124 | 125 | return nil 126 | } 127 | 128 | func (server *Server) readProfiles(conn *net.UnixConn, exename, funcname string, funcaddr uintptr) error { 129 | lastState := map[[32]uintptr]series.Sample{} 130 | 131 | var dec packet.Decoder 132 | for { 133 | err := dec.Read(conn) 134 | if err != nil { 135 | return fmt.Errorf("failed to read packet: %w", err) 136 | } 137 | 138 | unixnano := dec.Int64() 139 | count := dec.Uint32() 140 | 141 | profile := &Profile{ 142 | ExeName: exename, 143 | 144 | FuncName: funcname, 145 | FuncAddr: funcaddr, 146 | 147 | Time: time.Unix(0, unixnano), 148 | 149 | Records: make([]runtime.MemProfileRecord, count), 150 | } 151 | 152 | for i, rec := range profile.Records { 153 | var next series.Sample 154 | next.AllocBytes = dec.Int64() 155 | next.FreeBytes = dec.Int64() 156 | next.AllocObjects = dec.Int64() 157 | next.FreeObjects = dec.Int64() 158 | 159 | for i := 0; ; i++ { 160 | frame := dec.Uintptr() 161 | if frame == 0 { 162 | break 163 | } 164 | 165 | rec.Stack0[i] = frame 166 | } 167 | 168 | last, _ := lastState[rec.Stack0] 169 | lastState[rec.Stack0] = next 170 | 171 | rec.AllocBytes = next.AllocBytes - last.AllocBytes 172 | rec.FreeBytes = next.FreeBytes - last.FreeBytes 173 | rec.AllocObjects = next.AllocObjects - last.AllocObjects 174 | rec.FreeObjects = next.FreeObjects - last.FreeObjects 175 | 176 | profile.Records[i] = rec 177 | } 178 | 179 | server.profiles <- profile 180 | } 181 | } 182 | 183 | type Profile struct { 184 | ExeName string 185 | 186 | FuncName string 187 | FuncAddr uintptr 188 | 189 | Time time.Time 190 | 191 | Records []runtime.MemProfileRecord 192 | } 193 | -------------------------------------------------------------------------------- /view.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | "image/color" 6 | "sort" 7 | "strconv" 8 | "time" 9 | 10 | "gioui.org/app" 11 | "gioui.org/font/gofont" 12 | "gioui.org/io/system" 13 | "gioui.org/layout" 14 | "gioui.org/op" 15 | "gioui.org/op/clip" 16 | "gioui.org/op/paint" 17 | "gioui.org/unit" 18 | "gioui.org/widget/material" 19 | 20 | "loov.dev/allocview/internal/g" 21 | ) 22 | 23 | type Config struct { 24 | SampleDuration time.Duration 25 | SampleCount int 26 | } 27 | 28 | type View struct { 29 | Server *Server 30 | Summary *Summary 31 | 32 | series layout.List 33 | } 34 | 35 | func NewView(config Config, server *Server) *View { 36 | return &View{ 37 | Server: server, 38 | Summary: NewSummary(config), 39 | 40 | series: layout.List{Axis: layout.Vertical}, 41 | } 42 | } 43 | 44 | func (view *View) Run(w *app.Window) error { 45 | th := material.NewTheme(gofont.Collection()) 46 | var ops op.Ops 47 | for { 48 | select { 49 | case e := <-w.Events(): 50 | switch e := e.(type) { 51 | case system.DestroyEvent: 52 | return e.Err 53 | case system.FrameEvent: 54 | gtx := layout.NewContext(&ops, e) 55 | view.Update(gtx, th) 56 | e.Frame(gtx.Ops) 57 | } 58 | 59 | case profile := <-view.Server.Profiles(): 60 | view.Summary.Add(profile) 61 | w.Invalidate() 62 | } 63 | } 64 | } 65 | 66 | const ( 67 | SeriesHeight = 50 68 | SeriesPadding = 5 69 | CaptionHeight = 12 70 | SampleWidth = 3 71 | CaptionWidth = CaptionHeight * 20 72 | ) 73 | 74 | func (view *View) Update(gtx layout.Context, th *material.Theme) { 75 | paint.Fill(gtx.Ops, BackgroundColor) 76 | 77 | collection := view.Summary.Collection 78 | sort.SliceStable(collection.List, func(i, k int) bool { 79 | return collection.List[i].TotalAllocBytes > collection.List[k].TotalAllocBytes 80 | }) 81 | 82 | inset := layout.Inset{Bottom: unit.Dp(SeriesPadding)} 83 | 84 | view.series.Layout(gtx, len(collection.List), func(gtx layout.Context, i int) layout.Dimensions { 85 | return inset.Layout(gtx, func(gtx layout.Context) (dimension layout.Dimensions) { 86 | captionWidth := gtx.Dp(CaptionWidth) 87 | seriesHeight := gtx.Dp(SeriesHeight) 88 | series := collection.List[i] 89 | 90 | return layout.Flex{}.Layout(gtx, 91 | layout.Rigid(func(gtx layout.Context) layout.Dimensions { 92 | size := image.Pt(captionWidth, seriesHeight) 93 | FillRect(gtx.Ops, selectColor(i, RowBackgroundEvenH, RowBackgroundOddH), image.Rectangle{Max: size}) 94 | 95 | name := view.Summary.StackAsString(series.Stack) 96 | // TODO: don't wrap lines 97 | live := SizeToString(series.TotalAllocBytes) + " / " + strconv.Itoa(int(series.TotalAllocObjects)) 98 | label := material.Label(th, unit.Sp(CaptionHeight-3), name+live) 99 | label.Color = TextColor 100 | 101 | nowrap := gtx 102 | nowrap.Constraints.Min.X = 1024 103 | nowrap.Constraints.Max.X = 1024 104 | _ = label.Layout(nowrap) 105 | 106 | return layout.Dimensions{Size: size} 107 | }), 108 | layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { 109 | areaSize := image.Pt(gtx.Constraints.Max.X, seriesHeight) 110 | FillRect(gtx.Ops, selectColor(i, RowBackgroundEven, RowBackgroundOdd), image.Rectangle{Max: areaSize}) 111 | 112 | samples := areaSize.X / SampleWidth 113 | low := collection.SampleHead - samples 114 | if low < 0 { 115 | low = 0 116 | } 117 | high := low + samples 118 | 119 | max := series.MaxSampleBytes() 120 | 121 | prop := 1.0 / float32(max+1) 122 | scale := float32(areaSize.Y/2) / float32(max+1) 123 | 124 | corner := image.Point{ 125 | Y: areaSize.Y / 2, 126 | } 127 | for p := low; p < high; p++ { 128 | sample := series.Samples[p%collection.SampleCount] 129 | 130 | if p == collection.SampleHead { 131 | headColor := color.NRGBA{0x30, 0x30, 0x30, 0xFF} 132 | FillRect(gtx.Ops, headColor, image.Rectangle{ 133 | Min: image.Point{X: int(corner.X), Y: 0}, 134 | Max: image.Point{X: int(corner.X + SampleWidth), Y: int(areaSize.Y)}, 135 | }) 136 | continue 137 | } 138 | 139 | if sample.AllocBytes > 0 { 140 | c := g.HSL(0, 0.6, g.LerpClamp(float32(sample.AllocBytes)*prop, 0.3, 0.7)) 141 | FillRect(gtx.Ops, c, image.Rectangle{ 142 | Min: corner, 143 | Max: corner.Add(image.Point{ 144 | X: SampleWidth, 145 | Y: int(float32(sample.AllocBytes) * scale), 146 | }), 147 | }) 148 | } 149 | 150 | if sample.FreeBytes > 0 { 151 | c := g.HSL(0.3, 0.6, g.LerpClamp(float32(sample.FreeBytes)*prop, 0.3, 0.7)) 152 | FillRect(gtx.Ops, c, image.Rectangle{ 153 | Min: corner, 154 | Max: corner.Add(image.Point{ 155 | X: SampleWidth, 156 | Y: int(float32(-sample.FreeBytes) * scale), 157 | }), 158 | }) 159 | } 160 | 161 | corner.X += SampleWidth 162 | } 163 | 164 | return layout.Dimensions{Size: areaSize} 165 | }), 166 | ) 167 | }) 168 | }) 169 | } 170 | 171 | func FillRect(ops *op.Ops, c color.NRGBA, r image.Rectangle) { 172 | paint.FillShape(ops, c, clip.Rect(r).Op()) 173 | } 174 | 175 | var ( 176 | BackgroundColor = color.NRGBA{0x00, 0x00, 0x00, 0xFF} 177 | RowBackgroundEven = color.NRGBA{0x11, 0x11, 0x11, 0xFF} 178 | RowBackgroundEvenH = color.NRGBA{0x18, 0x18, 0x18, 0xFF} 179 | RowBackgroundOdd = color.NRGBA{0x22, 0x22, 0x22, 0xFF} 180 | RowBackgroundOddH = color.NRGBA{0x28, 0x28, 0x28, 0xFF} 181 | TextColor = color.NRGBA{0xFF, 0xFF, 0xFF, 0xFF} 182 | ) 183 | 184 | func selectColor(i int, values ...color.NRGBA) color.NRGBA { 185 | return values[i%len(values)] 186 | } 187 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 4 | eliasnaur.com/font v0.0.0-20220124212145-832bb8fc08c3 h1:djFprmHZgrSepsHAIRMp5UJn3PzsoTg9drI+BDmif5Q= 5 | eliasnaur.com/font v0.0.0-20220124212145-832bb8fc08c3/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA= 6 | gioui.org v0.0.0-20220726132227-f7bc744a24bf h1:8iFlrsx7NZs+mFsJqMKEaxYidxGUy5/n9iAmQkQZ7Ps= 7 | gioui.org v0.0.0-20220726132227-f7bc744a24bf/go.mod h1:WHoHbUjH91BJS2xkfps2AhKxji+9o3xwfsphGsCBfnM= 8 | gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= 9 | gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc= 10 | gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ= 11 | gioui.org/shader v1.0.6 h1:cvZmU+eODFR2545X+/8XucgZdTtEjR3QWW6W65b0q5Y= 12 | gioui.org/shader v1.0.6/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM= 13 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 14 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 15 | github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= 16 | github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= 17 | github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= 18 | github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= 19 | github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= 20 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 21 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 22 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 23 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 24 | github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 25 | github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 26 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 27 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 28 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 29 | github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= 30 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 31 | github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 32 | github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 33 | github.com/benoitkugler/pstokenizer v1.0.0/go.mod h1:l1G2Voirz0q/jj0TQfabNxVsa8HZXh/VMxFSRALWTiE= 34 | github.com/benoitkugler/textlayout v0.0.5/go.mod h1:puH4v13Uz7uIhIH0XMk5jgc8U3MXcn5r3VlV9K8n0D8= 35 | github.com/benoitkugler/textlayout v0.1.1 h1:hizE/085xAeY8q7gwV00uHR2Q27KYB2g1HW+UacXl68= 36 | github.com/benoitkugler/textlayout v0.1.1/go.mod h1:o+1hFV+JSHBC9qNLIuwVoLedERU7sBPgEFcuSgfvi/w= 37 | github.com/benoitkugler/textlayout-testdata v0.1.1 h1:AvFxBxpfrQd8v55qH59mZOJOQjtD6K2SFe9/HvnIbJk= 38 | github.com/benoitkugler/textlayout-testdata v0.1.1/go.mod h1:i/qZl09BbUOtd7Bu/W1CAubRwTWrEXWq6JwMkw8wYxo= 39 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 40 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 41 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 42 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 43 | github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= 44 | github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 45 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 46 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 47 | github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 48 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 49 | github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc= 50 | github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= 51 | github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 52 | github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 53 | github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 54 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 55 | github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 56 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 57 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 58 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 59 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 60 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 61 | github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 62 | github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= 63 | github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= 64 | github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= 65 | github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 66 | github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= 67 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 68 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 69 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 70 | github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= 71 | github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 72 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 73 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 74 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 75 | github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12 h1:1bjaB/5IIicfKpP4k0s30T2WEw//Kh00zULa8DQ0cxA= 76 | github.com/gioui/uax v0.2.1-0.20220325163150-e3d987515a12/go.mod h1:kDhBRTA/i3H46PVdhqcw26TdGSIj42TOKNWKY+Kipnw= 77 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 78 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 79 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 80 | github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 81 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 82 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 83 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 84 | github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= 85 | github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 86 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 87 | github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b h1:WINlj3ANt+CVrO2B4NGDHRlPvEWZPxjhb7z+JKypwXI= 88 | github.com/go-text/typesetting v0.0.0-20220411150340-35994bc27a7b/go.mod h1:ZNYu5saGoMOqtkVH5T8onTwhzenDUVszI+5WFHJRaxQ= 89 | github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= 90 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 91 | github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 92 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 93 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 94 | github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 95 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 96 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 97 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 98 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 99 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 100 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 101 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 102 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 103 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 104 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 105 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 106 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 107 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 108 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 109 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 110 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 111 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 112 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 113 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 114 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 115 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 116 | github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 117 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 118 | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= 119 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 120 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 121 | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 122 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 123 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 124 | github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 125 | github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= 126 | github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 127 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 128 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 129 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 130 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 131 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 132 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 133 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 134 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 135 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 136 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 137 | github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 138 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 139 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 140 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 141 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 142 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 143 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 144 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 145 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 146 | github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= 147 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 148 | github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 149 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 150 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 151 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 152 | github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 153 | github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 154 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 155 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 156 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 157 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 158 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 159 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 160 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 161 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 162 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 163 | github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= 164 | github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= 165 | github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 166 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 167 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 168 | github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 169 | github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= 170 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 171 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 172 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 173 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 174 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 175 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 176 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 177 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 178 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 179 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 180 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 181 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 182 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 183 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 184 | github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= 185 | github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= 186 | github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= 187 | github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= 188 | github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 189 | github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 190 | github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= 191 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 192 | github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 193 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 194 | github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 195 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 196 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 197 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 198 | github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= 199 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 200 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 201 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 202 | github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= 203 | github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 204 | github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= 205 | github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= 206 | github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 207 | github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= 208 | github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= 209 | github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= 210 | github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 211 | github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 212 | github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= 213 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 214 | github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= 215 | github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= 216 | github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= 217 | github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 218 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 219 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 220 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 221 | github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 222 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 223 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 224 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 225 | github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= 226 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 227 | github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= 228 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 229 | github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 230 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 231 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 232 | github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 233 | github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 234 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 235 | github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= 236 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 237 | github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 238 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 239 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 240 | github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= 241 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 242 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 243 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 244 | github.com/rs/zerolog v1.21.0/go.mod h1:ZPhntP/xmq1nnND05hhpAh2QMhSsA4UN3MGZ6O2J3hM= 245 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 246 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 247 | github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= 248 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 249 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 250 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 251 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 252 | github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 253 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 254 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 255 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 256 | github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= 257 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= 258 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 259 | github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 260 | github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= 261 | github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= 262 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 263 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 264 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 265 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 266 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 267 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 268 | github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 269 | github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= 270 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 271 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 272 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 273 | go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 274 | go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 275 | go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 276 | go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= 277 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 278 | go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= 279 | go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= 280 | go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= 281 | go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= 282 | go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= 283 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 284 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 285 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 286 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 287 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 288 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 289 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 290 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 291 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 292 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 293 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 294 | go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 295 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 296 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 297 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 298 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 299 | golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 300 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 301 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 302 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 303 | golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 304 | golang.org/x/exp v0.0.0-20210722180016-6781d3edade3 h1:IlrJD2AM5p8JhN/wVny9jt6gJ9hut2VALhSeZ3SYluk= 305 | golang.org/x/exp v0.0.0-20210722180016-6781d3edade3/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc= 306 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 307 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 308 | golang.org/x/image v0.0.0-20210504121937-7319ad40d33e/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 309 | golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 310 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= 311 | golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= 312 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 313 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 314 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 315 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 316 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 317 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 318 | golang.org/x/mobile v0.0.0-20201217150744-e6ae53a27f4f/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4= 319 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 320 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 321 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 322 | golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 323 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 324 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 325 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 326 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 327 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 328 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 329 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 330 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 331 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 332 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 333 | golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 334 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 335 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 336 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 337 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 338 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 339 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 340 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 341 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 342 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 343 | golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 344 | golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 345 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 346 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 347 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 348 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 349 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 350 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 351 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 352 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 353 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 354 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= 355 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 356 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 357 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 358 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 359 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 360 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 361 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 362 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 363 | golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 364 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 365 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 366 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 367 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 368 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 369 | golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 370 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 371 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 372 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 373 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 374 | golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 375 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 376 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 377 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 378 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 379 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 380 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 381 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= 382 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 383 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 384 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 385 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 386 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 387 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 388 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= 389 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 390 | golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 391 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 392 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 393 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 394 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 395 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 396 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 397 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 398 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 399 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 400 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 401 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 402 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 403 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 404 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 405 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 406 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 407 | golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 408 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 409 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 410 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 411 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 412 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 413 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 414 | google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= 415 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 416 | google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 417 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 418 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 419 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 420 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 421 | google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= 422 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 423 | google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= 424 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 425 | google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= 426 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 427 | google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 428 | google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 429 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 430 | google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 431 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 432 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 433 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 434 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 435 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 436 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 437 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 438 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 439 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 440 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 441 | gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 442 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 443 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 444 | gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 445 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 446 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 447 | gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= 448 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 449 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 450 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 451 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 452 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 453 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 454 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 455 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 456 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 457 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 458 | honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= 459 | sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= 460 | sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= 461 | --------------------------------------------------------------------------------