123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- package stack
- import (
- "fmt"
- "math"
- "net/url"
- "os"
- "path/filepath"
- "regexp"
- "sort"
- "strings"
- "unicode"
- "unicode/utf8"
- )
- const lockedToThread = "locked to thread"
- var (
-
-
-
-
- reRoutineHeader = regexp.MustCompile("^goroutine (\\d+) \\[([^\\]]+)\\]\\:\r?\n$")
- reMinutes = regexp.MustCompile("^(\\d+) minutes$")
- reUnavail = regexp.MustCompile("^(?:\t| +)goroutine running on other thread; stack unavailable")
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- reFile = regexp.MustCompile("^(?:\t| +)(\\?\\?|\\<autogenerated\\>|.+\\.(?:c|go|s))\\:(\\d+)(?:| \\+0x[0-9a-f]+)(?:| fp=0x[0-9a-f]+ sp=0x[0-9a-f]+)\r?\n$")
-
-
- reCreated = regexp.MustCompile("^created by (.+)\r?\n$")
- reFunc = regexp.MustCompile("^(.+)\\((.*)\\)\r?\n$")
- reElided = regexp.MustCompile("^\\.\\.\\.additional frames elided\\.\\.\\.\r?\n$")
- )
- type Func struct {
- Raw string
- }
- func (f *Func) String() string {
- s, _ := url.QueryUnescape(f.Raw)
- return s
- }
- func (f *Func) Name() string {
- parts := strings.SplitN(filepath.Base(f.Raw), ".", 2)
- if len(parts) == 1 {
- return parts[0]
- }
- return parts[1]
- }
- func (f *Func) PkgName() string {
- parts := strings.SplitN(filepath.Base(f.Raw), ".", 2)
- if len(parts) == 1 {
- return ""
- }
- s, _ := url.QueryUnescape(parts[0])
- return s
- }
- func (f *Func) PkgDotName() string {
- parts := strings.SplitN(filepath.Base(f.Raw), ".", 2)
- s, _ := url.QueryUnescape(parts[0])
- if len(parts) == 1 {
- return parts[0]
- }
- if s != "" || parts[1] != "" {
- return s + "." + parts[1]
- }
- return ""
- }
- func (f *Func) IsExported() bool {
- name := f.Name()
- parts := strings.Split(name, ".")
- r, _ := utf8.DecodeRuneInString(parts[len(parts)-1])
- if unicode.ToUpper(r) == r {
- return true
- }
- return f.PkgName() == "main" && name == "main"
- }
- type Arg struct {
- Value uint64
- Name string
- }
- func (a *Arg) IsPtr() bool {
-
- return a.Value > 16*1024*1024 && a.Value < math.MaxInt64
- }
- func (a *Arg) String() string {
- if a.Name != "" {
- return a.Name
- }
- if a.Value == 0 {
- return "0"
- }
- return fmt.Sprintf("0x%x", a.Value)
- }
- type Args struct {
- Values []Arg
- Processed []string
- Elided bool
- }
- func (a *Args) String() string {
- var v []string
- if len(a.Processed) != 0 {
- v = make([]string, 0, len(a.Processed))
- for _, item := range a.Processed {
- v = append(v, item)
- }
- } else {
- v = make([]string, 0, len(a.Values))
- for _, item := range a.Values {
- v = append(v, item.String())
- }
- }
- if a.Elided {
- v = append(v, "...")
- }
- return strings.Join(v, ", ")
- }
- func (a *Args) equal(r *Args) bool {
- if a.Elided != r.Elided || len(a.Values) != len(r.Values) {
- return false
- }
- for i, l := range a.Values {
- if l != r.Values[i] {
- return false
- }
- }
- return true
- }
- func (a *Args) similar(r *Args, similar Similarity) bool {
- if a.Elided != r.Elided || len(a.Values) != len(r.Values) {
- return false
- }
- if similar == AnyValue {
- return true
- }
- for i, l := range a.Values {
- switch similar {
- case ExactFlags, ExactLines:
- if l != r.Values[i] {
- return false
- }
- default:
- if l.IsPtr() != r.Values[i].IsPtr() || (!l.IsPtr() && l != r.Values[i]) {
- return false
- }
- }
- }
- return true
- }
- func (a *Args) merge(r *Args) Args {
- out := Args{
- Values: make([]Arg, len(a.Values)),
- Elided: a.Elided,
- }
- for i, l := range a.Values {
- if l != r.Values[i] {
- out.Values[i].Name = "*"
- out.Values[i].Value = l.Value
- } else {
- out.Values[i] = l
- }
- }
- return out
- }
- type Call struct {
- SrcPath string
- LocalSrcPath string
- Line int
- Func Func
- Args Args
- IsStdlib bool
- }
- func (c *Call) equal(r *Call) bool {
- return c.SrcPath == r.SrcPath && c.Line == r.Line && c.Func == r.Func && c.Args.equal(&r.Args)
- }
- func (c *Call) similar(r *Call, similar Similarity) bool {
- return c.SrcPath == r.SrcPath && c.Line == r.Line && c.Func == r.Func && c.Args.similar(&r.Args, similar)
- }
- func (c *Call) merge(r *Call) Call {
- return Call{
- SrcPath: c.SrcPath,
- Line: c.Line,
- Func: c.Func,
- Args: c.Args.merge(&r.Args),
- LocalSrcPath: c.LocalSrcPath,
- IsStdlib: c.IsStdlib,
- }
- }
- func (c *Call) SrcName() string {
- return filepath.Base(c.SrcPath)
- }
- func (c *Call) SrcLine() string {
- return fmt.Sprintf("%s:%d", c.SrcName(), c.Line)
- }
- func (c *Call) FullSrcLine() string {
- return fmt.Sprintf("%s:%d", c.SrcPath, c.Line)
- }
- func (c *Call) PkgSrc() string {
- return filepath.Join(filepath.Base(filepath.Dir(c.SrcPath)), c.SrcName())
- }
- func (c *Call) IsPkgMain() bool {
- return c.Func.PkgName() == "main"
- }
- const testMainSrc = "_test" + string(os.PathSeparator) + "_testmain.go"
- func (c *Call) updateLocations(goroot, localgoroot string, gopaths map[string]string) {
- if c.SrcPath != "" {
-
- if strings.HasPrefix(c.SrcPath, goroot) {
-
- c.LocalSrcPath = filepath.Join(localgoroot, c.SrcPath[len(goroot):])
- } else {
-
- c.LocalSrcPath = c.SrcPath
-
- for prefix, dest := range gopaths {
- if strings.HasPrefix(c.SrcPath, prefix) {
- c.LocalSrcPath = filepath.Join(dest, c.SrcPath[len(prefix):])
- break
- }
- }
- }
- }
-
- c.IsStdlib = (goroot != "" && strings.HasPrefix(c.SrcPath, goroot)) || c.PkgSrc() == testMainSrc
- }
- type Stack struct {
- Calls []Call
- Elided bool
- }
- func (s *Stack) equal(r *Stack) bool {
- if len(s.Calls) != len(r.Calls) || s.Elided != r.Elided {
- return false
- }
- for i := range s.Calls {
- if !s.Calls[i].equal(&r.Calls[i]) {
- return false
- }
- }
- return true
- }
- func (s *Stack) similar(r *Stack, similar Similarity) bool {
- if len(s.Calls) != len(r.Calls) || s.Elided != r.Elided {
- return false
- }
- for i := range s.Calls {
- if !s.Calls[i].similar(&r.Calls[i], similar) {
- return false
- }
- }
- return true
- }
- func (s *Stack) merge(r *Stack) *Stack {
-
- out := &Stack{
- Calls: make([]Call, len(s.Calls)),
- Elided: s.Elided,
- }
- for i := range s.Calls {
- out.Calls[i] = s.Calls[i].merge(&r.Calls[i])
- }
- return out
- }
- func (s *Stack) less(r *Stack) bool {
- lStdlib := 0
- lPrivate := 0
- for _, c := range s.Calls {
- if c.IsStdlib {
- lStdlib++
- } else {
- lPrivate++
- }
- }
- rStdlib := 0
- rPrivate := 0
- for _, s := range r.Calls {
- if s.IsStdlib {
- rStdlib++
- } else {
- rPrivate++
- }
- }
- if lPrivate > rPrivate {
- return true
- }
- if lPrivate < rPrivate {
- return false
- }
- if lStdlib > rStdlib {
- return false
- }
- if lStdlib < rStdlib {
- return true
- }
-
- for x := range s.Calls {
- if s.Calls[x].Func.Raw < r.Calls[x].Func.Raw {
- return true
- }
- if s.Calls[x].Func.Raw > r.Calls[x].Func.Raw {
- return true
- }
- if s.Calls[x].PkgSrc() < r.Calls[x].PkgSrc() {
- return true
- }
- if s.Calls[x].PkgSrc() > r.Calls[x].PkgSrc() {
- return true
- }
- if s.Calls[x].Line < r.Calls[x].Line {
- return true
- }
- if s.Calls[x].Line > r.Calls[x].Line {
- return true
- }
- }
- return false
- }
- func (s *Stack) updateLocations(goroot, localgoroot string, gopaths map[string]string) {
- for i := range s.Calls {
- s.Calls[i].updateLocations(goroot, localgoroot, gopaths)
- }
- }
- type Signature struct {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- State string
- CreatedBy Call
- SleepMin int
- SleepMax int
- Stack Stack
- Locked bool
- }
- func (s *Signature) equal(r *Signature) bool {
- if s.State != r.State || !s.CreatedBy.equal(&r.CreatedBy) || s.Locked != r.Locked || s.SleepMin != r.SleepMin || s.SleepMax != r.SleepMax {
- return false
- }
- return s.Stack.equal(&r.Stack)
- }
- func (s *Signature) similar(r *Signature, similar Similarity) bool {
- if s.State != r.State || !s.CreatedBy.similar(&r.CreatedBy, similar) {
- return false
- }
- if similar == ExactFlags && s.Locked != r.Locked {
- return false
- }
- return s.Stack.similar(&r.Stack, similar)
- }
- func (s *Signature) merge(r *Signature) *Signature {
- min := s.SleepMin
- if r.SleepMin < min {
- min = r.SleepMin
- }
- max := s.SleepMax
- if r.SleepMax > max {
- max = r.SleepMax
- }
- return &Signature{
- State: s.State,
- CreatedBy: s.CreatedBy,
- SleepMin: min,
- SleepMax: max,
- Stack: *s.Stack.merge(&r.Stack),
- Locked: s.Locked || r.Locked,
- }
- }
- func (s *Signature) less(r *Signature) bool {
- if s.Stack.less(&r.Stack) {
- return true
- }
- if r.Stack.less(&s.Stack) {
- return false
- }
- if s.Locked && !r.Locked {
- return true
- }
- if r.Locked && !s.Locked {
- return false
- }
- if s.State < r.State {
- return true
- }
- if s.State > r.State {
- return false
- }
- return false
- }
- func (s *Signature) SleepString() string {
- if s.SleepMax == 0 {
- return ""
- }
- if s.SleepMin != s.SleepMax {
- return fmt.Sprintf("%d~%d minutes", s.SleepMin, s.SleepMax)
- }
- return fmt.Sprintf("%d minutes", s.SleepMax)
- }
- func (s *Signature) CreatedByString(fullPath bool) string {
- created := s.CreatedBy.Func.PkgDotName()
- if created == "" {
- return ""
- }
- created += " @ "
- if fullPath {
- created += s.CreatedBy.FullSrcLine()
- } else {
- created += s.CreatedBy.SrcLine()
- }
- return created
- }
- func (s *Signature) updateLocations(goroot, localgoroot string, gopaths map[string]string) {
- s.CreatedBy.updateLocations(goroot, localgoroot, gopaths)
- s.Stack.updateLocations(goroot, localgoroot, gopaths)
- }
- type Goroutine struct {
- Signature
- ID int
- First bool
- }
- func nameArguments(goroutines []*Goroutine) {
-
- type object struct {
- args []*Arg
- inPrimary bool
- id int
- }
- objects := map[uint64]object{}
-
- for i := range goroutines {
- for j := range goroutines[i].Stack.Calls {
- for k := range goroutines[i].Stack.Calls[j].Args.Values {
- arg := goroutines[i].Stack.Calls[j].Args.Values[k]
- if arg.IsPtr() {
- objects[arg.Value] = object{
- args: append(objects[arg.Value].args, &goroutines[i].Stack.Calls[j].Args.Values[k]),
- inPrimary: objects[arg.Value].inPrimary || i == 0,
- }
- }
- }
- }
-
- }
- order := make(uint64Slice, 0, len(objects)/2)
- for k, obj := range objects {
- if len(obj.args) > 1 && obj.inPrimary {
- order = append(order, k)
- }
- }
- sort.Sort(order)
- nextID := 1
- for _, k := range order {
- for _, arg := range objects[k].args {
- arg.Name = fmt.Sprintf("#%d", nextID)
- }
- nextID++
- }
-
- order = make(uint64Slice, 0, len(objects))
- for k := range objects {
- order = append(order, k)
- }
- sort.Sort(order)
- for _, k := range order {
-
-
- if objects[k].inPrimary {
- continue
- }
- for _, arg := range objects[k].args {
- arg.Name = fmt.Sprintf("#%d", nextID)
- }
- nextID++
- }
- }
- type uint64Slice []uint64
- func (a uint64Slice) Len() int { return len(a) }
- func (a uint64Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
- func (a uint64Slice) Less(i, j int) bool { return a[i] < a[j] }
|