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  }