140 lines
3.6 KiB
Go
140 lines
3.6 KiB
Go
// Copyright 2016 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 (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Palette defines the color used.
|
|
//
|
|
// An empty object Palette{} can be used to disable coloring.
|
|
type Palette struct {
|
|
EOLReset string
|
|
|
|
// Routine header.
|
|
RoutineFirst string // The first routine printed.
|
|
Routine string // Following routines.
|
|
CreatedBy string
|
|
|
|
// Call line.
|
|
Package string
|
|
SourceFile string
|
|
FunctionStdLib string
|
|
FunctionStdLibExported string
|
|
FunctionMain string
|
|
FunctionOther string
|
|
FunctionOtherExported string
|
|
Arguments string
|
|
}
|
|
|
|
// CalcLengths returns the maximum length of the source lines and package names.
|
|
func CalcLengths(buckets Buckets, fullPath bool) (int, int) {
|
|
srcLen := 0
|
|
pkgLen := 0
|
|
for _, bucket := range buckets {
|
|
for _, line := range bucket.Signature.Stack.Calls {
|
|
l := 0
|
|
if fullPath {
|
|
l = len(line.FullSourceLine())
|
|
} else {
|
|
l = len(line.SourceLine())
|
|
}
|
|
if l > srcLen {
|
|
srcLen = l
|
|
}
|
|
l = len(line.Func.PkgName())
|
|
if l > pkgLen {
|
|
pkgLen = l
|
|
}
|
|
}
|
|
}
|
|
return srcLen, pkgLen
|
|
}
|
|
|
|
// functionColor returns the color to be used for the function name based on
|
|
// the type of package the function is in.
|
|
func (p *Palette) functionColor(line *Call) string {
|
|
if line.IsStdlib() {
|
|
if line.Func.IsExported() {
|
|
return p.FunctionStdLibExported
|
|
}
|
|
return p.FunctionStdLib
|
|
} else if line.IsPkgMain() {
|
|
return p.FunctionMain
|
|
} else if line.Func.IsExported() {
|
|
return p.FunctionOtherExported
|
|
}
|
|
return p.FunctionOther
|
|
}
|
|
|
|
// routineColor returns the color for the header of the goroutines bucket.
|
|
func (p *Palette) routineColor(bucket *Bucket, multipleBuckets bool) string {
|
|
if bucket.First() && multipleBuckets {
|
|
return p.RoutineFirst
|
|
}
|
|
return p.Routine
|
|
}
|
|
|
|
// BucketHeader prints the header of a goroutine signature.
|
|
func (p *Palette) BucketHeader(bucket *Bucket, fullPath, multipleBuckets bool) string {
|
|
extra := ""
|
|
if bucket.SleepMax != 0 {
|
|
if bucket.SleepMin != bucket.SleepMax {
|
|
extra += fmt.Sprintf(" [%d~%d minutes]", bucket.SleepMin, bucket.SleepMax)
|
|
} else {
|
|
extra += fmt.Sprintf(" [%d minutes]", bucket.SleepMax)
|
|
}
|
|
}
|
|
if bucket.Locked {
|
|
extra += " [locked]"
|
|
}
|
|
created := bucket.CreatedBy.Func.PkgDotName()
|
|
if created != "" {
|
|
created += " @ "
|
|
if fullPath {
|
|
created += bucket.CreatedBy.FullSourceLine()
|
|
} else {
|
|
created += bucket.CreatedBy.SourceLine()
|
|
}
|
|
extra += p.CreatedBy + " [Created by " + created + "]"
|
|
}
|
|
return fmt.Sprintf(
|
|
"%s%d: %s%s%s\n",
|
|
p.routineColor(bucket, multipleBuckets), len(bucket.Routines),
|
|
bucket.State, extra,
|
|
p.EOLReset)
|
|
}
|
|
|
|
// callLine prints one stack line.
|
|
func (p *Palette) callLine(line *Call, srcLen, pkgLen int, fullPath bool) string {
|
|
src := ""
|
|
if fullPath {
|
|
src = line.FullSourceLine()
|
|
} else {
|
|
src = line.SourceLine()
|
|
}
|
|
return fmt.Sprintf(
|
|
" %s%-*s %s%-*s %s%s%s(%s)%s",
|
|
p.Package, pkgLen, line.Func.PkgName(),
|
|
p.SourceFile, srcLen, src,
|
|
p.functionColor(line), line.Func.Name(),
|
|
p.Arguments, line.Args,
|
|
p.EOLReset)
|
|
}
|
|
|
|
// StackLines prints one complete stack trace, without the header.
|
|
func (p *Palette) StackLines(signature *Signature, srcLen, pkgLen int, fullPath bool) string {
|
|
out := make([]string, len(signature.Stack.Calls))
|
|
for i := range signature.Stack.Calls {
|
|
out[i] = p.callLine(&signature.Stack.Calls[i], srcLen, pkgLen, fullPath)
|
|
}
|
|
if signature.Stack.Elided {
|
|
out = append(out, " (...)")
|
|
}
|
|
return strings.Join(out, "\n") + "\n"
|
|
}
|