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