github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/server/debug/goroutineui/dump.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package goroutineui 12 13 import ( 14 "bytes" 15 "io" 16 "io/ioutil" 17 "runtime" 18 "sort" 19 "strings" 20 "time" 21 22 "github.com/maruel/panicparse/stack" 23 ) 24 25 // stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. 26 func stacks() []byte { 27 // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. 28 var trace []byte 29 for n := 1 << 20; /* 1mb */ n <= (1 << 29); /* 512mb */ n *= 2 { 30 trace = make([]byte, n) 31 nbytes := runtime.Stack(trace, true /* all */) 32 if nbytes < len(trace) { 33 return trace[:nbytes] 34 } 35 } 36 return trace 37 } 38 39 // A Dump wraps a goroutine dump with functionality to output through panicparse. 40 type Dump struct { 41 err error 42 43 now time.Time 44 buckets []*stack.Bucket 45 } 46 47 // NewDump grabs a goroutine dump and associates it with the supplied time. 48 func NewDump(now time.Time) Dump { 49 return NewDumpFromBytes(now, stacks()) 50 } 51 52 // NewDumpFromBytes is like NewDump, but treats the supplied bytes as a goroutine 53 // dump. 54 func NewDumpFromBytes(now time.Time, b []byte) Dump { 55 c, err := stack.ParseDump(bytes.NewReader(b), ioutil.Discard, true /* guesspaths */) 56 if err != nil { 57 return Dump{err: err} 58 } 59 return Dump{now: now, buckets: stack.Aggregate(c.Goroutines, stack.AnyValue)} 60 } 61 62 // SortCountDesc rearranges the goroutine buckets such that higher multiplicities 63 // appear earlier. 64 func (d Dump) SortCountDesc() { 65 sort.Slice(d.buckets, func(i, j int) bool { 66 a, b := d.buckets[i], d.buckets[j] 67 return len(a.IDs) > len(b.IDs) 68 }) 69 } 70 71 // SortWaitDesc rearranges the goroutine buckets such that goroutines that have 72 // longer wait times appear earlier. 73 func (d Dump) SortWaitDesc() { 74 sort.Slice(d.buckets, func(i, j int) bool { 75 a, b := d.buckets[i], d.buckets[j] 76 return a.SleepMax > b.SleepMax 77 }) 78 } 79 80 // HTML writes the rendered output of panicparse into the supplied Writer. 81 func (d Dump) HTML(w io.Writer) error { 82 if d.err != nil { 83 return d.err 84 } 85 return writeToHTML(w, d.buckets, d.now) 86 } 87 88 // HTMLString is like HTML, but returns a string. If an error occurs, its string 89 // representation is returned. 90 func (d Dump) HTMLString() string { 91 var w strings.Builder 92 if err := d.HTML(&w); err != nil { 93 return err.Error() 94 } 95 return w.String() 96 }