@@ -27,6 +27,7 @@ import (
2727 "os"
2828 "os/user"
2929 "path/filepath"
30+ "regexp"
3031 "runtime"
3132 "runtime/debug"
3233 "runtime/pprof"
@@ -35,6 +36,7 @@ import (
3536 "time"
3637
3738 "github.com/ethereum/go-ethereum/log"
39+ "github.com/hashicorp/go-bexpr"
3840)
3941
4042// Handler is the global debugging handler.
@@ -189,10 +191,44 @@ func (*HandlerT) WriteMemProfile(file string) error {
189191 return writeProfile ("heap" , file )
190192}
191193
192- // Stacks returns a printed representation of the stacks of all goroutines.
193- func (* HandlerT ) Stacks () string {
194+ // Stacks returns a printed representation of the stacks of all goroutines. It
195+ // also permits the following optional filters to be used:
196+ // - filter: boolean expression of packages to filter for
197+ func (* HandlerT ) Stacks (filter * string ) string {
194198 buf := new (bytes.Buffer )
195199 pprof .Lookup ("goroutine" ).WriteTo (buf , 2 )
200+
201+ // If any filtering was requested, execute them now
202+ if filter != nil && len (* filter ) > 0 {
203+ expanded := * filter
204+
205+ // The input filter is a logical expression of package names. Transform
206+ // it into a proper boolean expression that can be fed into a parser and
207+ // interpreter:
208+ //
209+ // E.g. (eth || snap) && !p2p -> (eth in Value || snap in Value) && p2p not in Value
210+ expanded = regexp .MustCompile ("[:/\\ .A-Za-z0-9_-]+" ).ReplaceAllString (expanded , "`$0` in Value" )
211+ expanded = regexp .MustCompile ("!(`[:/\\ .A-Za-z0-9_-]+`)" ).ReplaceAllString (expanded , "$1 not" )
212+ expanded = strings .Replace (expanded , "||" , "or" , - 1 )
213+ expanded = strings .Replace (expanded , "&&" , "and" , - 1 )
214+ log .Info ("Expanded filter expression" , "filter" , * filter , "expanded" , expanded )
215+
216+ expr , err := bexpr .CreateEvaluator (expanded )
217+ if err != nil {
218+ log .Error ("Failed to parse filter expression" , "expanded" , expanded , "err" , err )
219+ return ""
220+ }
221+ // Split the goroutine dump into segments and filter each
222+ dump := buf .String ()
223+ buf .Reset ()
224+
225+ for _ , trace := range strings .Split (dump , "\n \n " ) {
226+ if ok , _ := expr .Evaluate (map [string ]string {"Value" : trace }); ok {
227+ buf .WriteString (trace )
228+ buf .WriteString ("\n \n " )
229+ }
230+ }
231+ }
196232 return buf .String ()
197233}
198234
0 commit comments