github.com/goplus/gop@v1.2.6/x/jsonrpc2/internal/stack/process.go (about) 1 // Copyright 2020 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package stack 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "runtime" 12 "sort" 13 ) 14 15 // Capture get the current stack traces from the runtime. 16 func Capture() Dump { 17 buf := make([]byte, 2<<20) 18 buf = buf[:runtime.Stack(buf, true)] 19 scanner := NewScanner(bytes.NewReader(buf)) 20 dump, _ := Parse(scanner) 21 return dump 22 } 23 24 // Summarize a dump for easier consumption. 25 // This collates goroutines with equivalent stacks. 26 func Summarize(dump Dump) Summary { 27 s := Summary{ 28 Total: len(dump), 29 } 30 for _, gr := range dump { 31 s.addGoroutine(gr) 32 } 33 return s 34 } 35 36 // Process and input stream to an output stream, summarizing any stacks that 37 // are detected in place. 38 func Process(out io.Writer, in io.Reader) error { 39 scanner := NewScanner(in) 40 for { 41 dump, err := Parse(scanner) 42 summary := Summarize(dump) 43 switch { 44 case len(dump) > 0: 45 fmt.Fprintf(out, "%+v\n\n", summary) 46 case err != nil: 47 return err 48 case scanner.Done(): 49 return scanner.Err() 50 default: 51 // must have been a line that is not part of a dump 52 fmt.Fprintln(out, scanner.Next()) 53 } 54 } 55 } 56 57 // Diff calculates the delta between two dumps. 58 func Diff(before, after Dump) Delta { 59 result := Delta{} 60 processed := make(map[int]bool) 61 for _, gr := range before { 62 processed[gr.ID] = false 63 } 64 for _, gr := range after { 65 if _, found := processed[gr.ID]; found { 66 result.Shared = append(result.Shared, gr) 67 } else { 68 result.After = append(result.After, gr) 69 } 70 processed[gr.ID] = true 71 } 72 for _, gr := range before { 73 if done := processed[gr.ID]; !done { 74 result.Before = append(result.Before, gr) 75 } 76 } 77 return result 78 } 79 80 // TODO: do we want to allow contraction of stacks before comparison? 81 func (s *Summary) addGoroutine(gr Goroutine) { 82 index := sort.Search(len(s.Calls), func(i int) bool { 83 return !s.Calls[i].Stack.less(gr.Stack) 84 }) 85 if index >= len(s.Calls) || !s.Calls[index].Stack.equal(gr.Stack) { 86 // insert new stack, first increase the length 87 s.Calls = append(s.Calls, Call{}) 88 // move the top part upward to make space 89 copy(s.Calls[index+1:], s.Calls[index:]) 90 // insert the new call 91 s.Calls[index] = Call{ 92 Stack: gr.Stack, 93 } 94 } 95 // merge the goroutine into the matched call 96 s.Calls[index].merge(gr) 97 } 98 99 // TODO: do we want other grouping strategies? 100 func (c *Call) merge(gr Goroutine) { 101 for i := range c.Groups { 102 canditate := &c.Groups[i] 103 if canditate.State == gr.State { 104 canditate.Goroutines = append(canditate.Goroutines, gr) 105 return 106 } 107 } 108 c.Groups = append(c.Groups, Group{ 109 State: gr.State, 110 Goroutines: []Goroutine{gr}, 111 }) 112 }