internal: support optional filter expression for debug.stacks (#23605)
* internal: support optional filter expression for debug.stacks * internal/debug: fix string regexp * internal/debug: support searching for line numbers too
This commit is contained in:
		| @@ -27,6 +27,7 @@ import ( | ||||
| 	"os" | ||||
| 	"os/user" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"runtime" | ||||
| 	"runtime/debug" | ||||
| 	"runtime/pprof" | ||||
| @@ -35,6 +36,7 @@ import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/ethereum/go-ethereum/log" | ||||
| 	"github.com/hashicorp/go-bexpr" | ||||
| ) | ||||
|  | ||||
| // Handler is the global debugging handler. | ||||
| @@ -189,10 +191,44 @@ func (*HandlerT) WriteMemProfile(file string) error { | ||||
| 	return writeProfile("heap", file) | ||||
| } | ||||
|  | ||||
| // Stacks returns a printed representation of the stacks of all goroutines. | ||||
| func (*HandlerT) Stacks() string { | ||||
| // Stacks returns a printed representation of the stacks of all goroutines. It | ||||
| // also permits the following optional filters to be used: | ||||
| //   - filter: boolean expression of packages to filter for | ||||
| func (*HandlerT) Stacks(filter *string) string { | ||||
| 	buf := new(bytes.Buffer) | ||||
| 	pprof.Lookup("goroutine").WriteTo(buf, 2) | ||||
|  | ||||
| 	// If any filtering was requested, execute them now | ||||
| 	if filter != nil && len(*filter) > 0 { | ||||
| 		expanded := *filter | ||||
|  | ||||
| 		// The input filter is a logical expression of package names. Transform | ||||
| 		// it into a proper boolean expression that can be fed into a parser and | ||||
| 		// interpreter: | ||||
| 		// | ||||
| 		// E.g. (eth || snap) && !p2p -> (eth in Value || snap in Value) && p2p not in Value | ||||
| 		expanded = regexp.MustCompile("[:/\\.A-Za-z0-9_-]+").ReplaceAllString(expanded, "`$0` in Value") | ||||
| 		expanded = regexp.MustCompile("!(`[:/\\.A-Za-z0-9_-]+`)").ReplaceAllString(expanded, "$1 not") | ||||
| 		expanded = strings.Replace(expanded, "||", "or", -1) | ||||
| 		expanded = strings.Replace(expanded, "&&", "and", -1) | ||||
| 		log.Info("Expanded filter expression", "filter", *filter, "expanded", expanded) | ||||
|  | ||||
| 		expr, err := bexpr.CreateEvaluator(expanded) | ||||
| 		if err != nil { | ||||
| 			log.Error("Failed to parse filter expression", "expanded", expanded, "err", err) | ||||
| 			return "" | ||||
| 		} | ||||
| 		// Split the goroutine dump into segments and filter each | ||||
| 		dump := buf.String() | ||||
| 		buf.Reset() | ||||
|  | ||||
| 		for _, trace := range strings.Split(dump, "\n\n") { | ||||
| 			if ok, _ := expr.Evaluate(map[string]string{"Value": trace}); ok { | ||||
| 				buf.WriteString(trace) | ||||
| 				buf.WriteString("\n\n") | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return buf.String() | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user