github.com/matrixorigin/matrixone@v1.2.0/pkg/util/debug/goroutine/analyze.go (about) 1 // Copyright 2023 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package goroutine 16 17 import ( 18 "bytes" 19 "sort" 20 21 "github.com/matrixorigin/matrixone/pkg/util/profile" 22 ) 23 24 var ( 25 defaultAnalyzer = &analyzer{pools: map[string]int{}} 26 ) 27 28 func init() { 29 defaultAnalyzer.addGoroutinePool("created by github.com/panjf2000/ants/v2.(*goWorker).run", 2) 30 defaultAnalyzer.addGoroutinePool("created by github.com/matrixorigin/matrixone/pkg/common/stopper.(*Stopper).doRunCancelableTask", 2) 31 defaultAnalyzer.addGoroutinePool("created by github.com/lni/goutils/syncutil.(*Stopper).runWorker", 2) 32 } 33 34 func GetAnalyzer() *analyzer { 35 return defaultAnalyzer 36 } 37 38 type analyzer struct { 39 pools map[string]int 40 } 41 42 func (z *analyzer) ParseSystem() []Goroutine { 43 var buf bytes.Buffer 44 if err := profile.ProfileGoroutine(&buf, 2); err != nil { 45 panic("impossible") 46 } 47 return z.Parse(buf.Bytes()) 48 } 49 50 func (z *analyzer) Parse(data []byte) []Goroutine { 51 gs := parse(data) 52 for i := range gs { 53 if v, ok := z.pools[gs[i].Last()]; ok { 54 gs[i].realFuncLevel = v 55 } 56 } 57 return gs 58 } 59 60 func (z *analyzer) GroupAnalyze(gs []Goroutine) AnalyzeResult { 61 result := AnalyzeResult{ 62 Goroutines: gs, 63 } 64 result.createGroups = z.group( 65 gs, 66 nil, 67 func(g Goroutine) string { 68 v, _ := g.CreateBy() 69 return v 70 }) 71 for _, values := range result.createGroups { 72 groups := z.group( 73 gs, 74 values, 75 func(g Goroutine) string { 76 return g.files[0] 77 }) 78 result.firstMethodGroups = append(result.firstMethodGroups, groups) 79 } 80 return result 81 } 82 83 func (z *analyzer) group( 84 gs []Goroutine, 85 indexes []int, 86 fn func(Goroutine) string) [][]int { 87 var groups [][]int 88 find := func(c string) int { 89 for i := range groups { 90 m := fn(gs[groups[i][0]]) 91 if m == c { 92 return i 93 } 94 } 95 return -1 96 } 97 handle := func(i int) { 98 c := fn(gs[i]) 99 idx := find(c) 100 if idx == -1 { 101 groups = append(groups, []int{i}) 102 return 103 } 104 groups[idx] = append(groups[idx], i) 105 } 106 107 if len(indexes) == 0 { 108 for i := range gs { 109 handle(i) 110 } 111 } else { 112 for _, i := range indexes { 113 handle(i) 114 } 115 } 116 sort.Slice(groups, func(i, j int) bool { 117 return len(groups[i]) > len(groups[j]) 118 }) 119 return groups 120 } 121 122 func (z *analyzer) addGoroutinePool( 123 name string, 124 realFuncLevel int) { 125 z.pools[name] = realFuncLevel 126 }