mirror of
				https://github.com/minio/minio.git
				synced 2025-10-31 00:01:27 +01:00 
			
		
		
		
	there can be a sudden spike in tiny allocations, due to too much auditing being done, also don't hang on the ``` h.logCh <- entry ``` after initializing workers if you do not have a way to dequeue for some reason.
		
			
				
	
	
		
			161 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2015-2024 MinIO, Inc.
 | |
| //
 | |
| // This file is part of MinIO Object Storage stack
 | |
| //
 | |
| // This program is free software: you can redistribute it and/or modify
 | |
| // it under the terms of the GNU Affero General Public License as published by
 | |
| // the Free Software Foundation, either version 3 of the License, or
 | |
| // (at your option) any later version.
 | |
| //
 | |
| // This program is distributed in the hope that it will be useful
 | |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| // GNU Affero General Public License for more details.
 | |
| //
 | |
| // You should have received a copy of the GNU Affero General Public License
 | |
| // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| package main
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"bytes"
 | |
| 	"flag"
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"math"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"regexp"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	goroutinesRE, searchRE *regexp.Regexp
 | |
| 
 | |
| 	// User input flags
 | |
| 	searchText           string
 | |
| 	goTime, less, margin time.Duration
 | |
| )
 | |
| 
 | |
| func init() {
 | |
| 	flag.DurationVar(&less, "less", 0, "goroutine waiting less than the specified time")
 | |
| 	flag.DurationVar(&goTime, "time", 0, "goroutine waiting for exactly the specified time")
 | |
| 	flag.DurationVar(&margin, "margin", 0, "margin time")
 | |
| 	flag.StringVar(&searchText, "search", "", "Regex to search for a text in one goroutine stacktrace")
 | |
| }
 | |
| 
 | |
| func parseGoroutineType2(path string) (map[time.Duration][]string, error) {
 | |
| 	f, err := os.Open(path)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	bf := bytes.Buffer{}
 | |
| 
 | |
| 	save := func(s string) {
 | |
| 		bf.WriteString(s + "\n")
 | |
| 	}
 | |
| 	reset := func() {
 | |
| 		bf.Reset()
 | |
| 	}
 | |
| 
 | |
| 	ret := make(map[time.Duration][]string)
 | |
| 
 | |
| 	s := bufio.NewScanner(f)
 | |
| 	s.Split(bufio.ScanLines)
 | |
| 
 | |
| 	var (
 | |
| 		t            time.Duration
 | |
| 		skip, record bool
 | |
| 	)
 | |
| 
 | |
| 	for s.Scan() {
 | |
| 		line := s.Text()
 | |
| 		switch {
 | |
| 		case skip && line != "":
 | |
| 		case skip && line == "":
 | |
| 			skip = false
 | |
| 		case record && line == "":
 | |
| 			stackTrace := bf.String()
 | |
| 			reset()
 | |
| 			record = false
 | |
| 			if searchRE == nil || searchRE.MatchString(stackTrace) {
 | |
| 				ret[t] = append(ret[t], stackTrace)
 | |
| 			}
 | |
| 		case record:
 | |
| 			save(line)
 | |
| 		default:
 | |
| 			z := goroutinesRE.FindStringSubmatch(line)
 | |
| 			if len(z) == 3 {
 | |
| 				if z[2] != "" {
 | |
| 					a, _ := strconv.Atoi(z[2])
 | |
| 					t = time.Duration(a) * time.Minute
 | |
| 					save(line)
 | |
| 					record = true
 | |
| 				} else {
 | |
| 					skip = true
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret, nil
 | |
| }
 | |
| 
 | |
| const helpUsage = `
 | |
| 
 | |
| At least one argument is required to run this tool.
 | |
| 
 | |
| EXAMPLE:
 | |
|      ./pprofgoparser --time 50m --margin 1m /path/to/*-goroutines-before,debug=2.txt
 | |
| `
 | |
| 
 | |
| func main() {
 | |
| 	flag.Parse()
 | |
| 
 | |
| 	if len(flag.Args()) == 0 {
 | |
| 		log.Fatal(helpUsage)
 | |
| 	}
 | |
| 
 | |
| 	var err error
 | |
| 
 | |
| 	goroutinesRE = regexp.MustCompile(`^goroutine [0-9]+ \[[^,]+(, ([0-9]+) minutes)?\]:$`)
 | |
| 
 | |
| 	if searchText != "" {
 | |
| 		searchRE, err = regexp.Compile(searchText)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for _, arg := range flag.Args() {
 | |
| 		if !strings.HasSuffix(arg, "-goroutines-before,debug=2.txt") {
 | |
| 			continue
 | |
| 		}
 | |
| 		r, err := parseGoroutineType2(arg)
 | |
| 		if err != nil {
 | |
| 			log.Fatal(err)
 | |
| 		}
 | |
| 
 | |
| 		profFName := path.Base(arg)
 | |
| 		fmt.Println(strings.Repeat("=", len(profFName)))
 | |
| 		fmt.Println(profFName)
 | |
| 		fmt.Println(strings.Repeat("=", len(profFName)))
 | |
| 		fmt.Println("")
 | |
| 
 | |
| 		for t, stacks := range r {
 | |
| 			if less != 0 && t >= less {
 | |
| 				continue
 | |
| 			}
 | |
| 			if goTime == 0 || math.Abs(float64(t)-float64(goTime)) <= float64(margin) {
 | |
| 				for _, stack := range stacks {
 | |
| 					fmt.Println(stack)
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |