mirror of https://github.com/miguelmota/cointop
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
106 lines
2.7 KiB
Go
106 lines
2.7 KiB
Go
6 years ago
|
// Copyright 2015 Marc-Antoine Ruel. All rights reserved.
|
||
|
// Use of this source code is governed under the Apache License, Version 2.0
|
||
|
// that can be found in the LICENSE file.
|
||
|
|
||
|
package stack
|
||
|
|
||
|
import (
|
||
|
"sort"
|
||
|
)
|
||
|
|
||
|
// Similarity is the level at which two call lines arguments must match to be
|
||
|
// considered similar enough to coalesce them.
|
||
|
type Similarity int
|
||
|
|
||
|
const (
|
||
|
// ExactFlags requires same bits (e.g. Locked).
|
||
|
ExactFlags Similarity = iota
|
||
|
// ExactLines requests the exact same arguments on the call line.
|
||
|
ExactLines
|
||
|
// AnyPointer considers different pointers a similar call line.
|
||
|
AnyPointer
|
||
|
// AnyValue accepts any value as similar call line.
|
||
|
AnyValue
|
||
|
)
|
||
|
|
||
|
// Aggregate merges similar goroutines into buckets.
|
||
|
//
|
||
|
// The buckets are ordered in library provided order of relevancy. You can
|
||
|
// reorder at your chosing.
|
||
|
func Aggregate(goroutines []*Goroutine, similar Similarity) []*Bucket {
|
||
|
type count struct {
|
||
|
ids []int
|
||
|
first bool
|
||
|
}
|
||
|
b := map[*Signature]*count{}
|
||
|
// O(n²). Fix eventually.
|
||
|
for _, routine := range goroutines {
|
||
|
found := false
|
||
|
for key, c := range b {
|
||
|
// When a match is found, this effectively drops the other goroutine ID.
|
||
|
if key.similar(&routine.Signature, similar) {
|
||
|
found = true
|
||
|
c.ids = append(c.ids, routine.ID)
|
||
|
c.first = c.first || routine.First
|
||
|
if !key.equal(&routine.Signature) {
|
||
|
// Almost but not quite equal. There's different pointers passed
|
||
|
// around but the same values. Zap out the different values.
|
||
|
newKey := key.merge(&routine.Signature)
|
||
|
b[newKey] = c
|
||
|
delete(b, key)
|
||
|
}
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !found {
|
||
|
// Create a copy of the Signature, since it will be mutated.
|
||
|
key := &Signature{}
|
||
|
*key = routine.Signature
|
||
|
b[key] = &count{ids: []int{routine.ID}, first: routine.First}
|
||
|
}
|
||
|
}
|
||
|
out := make(buckets, 0, len(b))
|
||
|
for signature, c := range b {
|
||
|
sort.Ints(c.ids)
|
||
|
out = append(out, &Bucket{Signature: *signature, IDs: c.ids, First: c.first})
|
||
|
}
|
||
|
sort.Sort(out)
|
||
|
return out
|
||
|
}
|
||
|
|
||
|
// Bucket is a stack trace signature and the list of goroutines that fits this
|
||
|
// signature.
|
||
|
type Bucket struct {
|
||
|
Signature
|
||
|
// IDs is the ID of each Goroutine with this Signature.
|
||
|
IDs []int
|
||
|
// First is true if this Bucket contains the first goroutine, e.g. the one
|
||
|
// Signature that likely generated the panic() call, if any.
|
||
|
First bool
|
||
|
}
|
||
|
|
||
|
// less does reverse sort.
|
||
|
func (b *Bucket) less(r *Bucket) bool {
|
||
|
if b.First || r.First {
|
||
|
return b.First
|
||
|
}
|
||
|
return b.Signature.less(&r.Signature)
|
||
|
}
|
||
|
|
||
|
//
|
||
|
|
||
|
// buckets is a list of Bucket sorted by repeation count.
|
||
|
type buckets []*Bucket
|
||
|
|
||
|
func (b buckets) Len() int {
|
||
|
return len(b)
|
||
|
}
|
||
|
|
||
|
func (b buckets) Less(i, j int) bool {
|
||
|
return b[i].less(b[j])
|
||
|
}
|
||
|
|
||
|
func (b buckets) Swap(i, j int) {
|
||
|
b[j], b[i] = b[i], b[j]
|
||
|
}
|