+++ /dev/null
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package stack
-
-import (
- "bytes"
- "fmt"
- "io"
- "runtime"
- "sort"
-)
-
-// Capture get the current stack traces from the runtime.
-func Capture() Dump {
- buf := make([]byte, 2<<20)
- buf = buf[:runtime.Stack(buf, true)]
- scanner := NewScanner(bytes.NewReader(buf))
- dump, _ := Parse(scanner)
- return dump
-}
-
-// Summarize a dump for easier consumption.
-// This collates goroutines with equivalent stacks.
-func Summarize(dump Dump) Summary {
- s := Summary{
- Total: len(dump),
- }
- for _, gr := range dump {
- s.addGoroutine(gr)
- }
- return s
-}
-
-// Process and input stream to an output stream, summarizing any stacks that
-// are detected in place.
-func Process(out io.Writer, in io.Reader) error {
- scanner := NewScanner(in)
- for {
- dump, err := Parse(scanner)
- summary := Summarize(dump)
- switch {
- case len(dump) > 0:
- fmt.Fprintf(out, "%+v\n\n", summary)
- case err != nil:
- return err
- case scanner.Done():
- return scanner.Err()
- default:
- // must have been a line that is not part of a dump
- fmt.Fprintln(out, scanner.Next())
- }
- }
-}
-
-// Diff calculates the delta between two dumps.
-func Diff(before, after Dump) Delta {
- result := Delta{}
- processed := make(map[int]bool)
- for _, gr := range before {
- processed[gr.ID] = false
- }
- for _, gr := range after {
- if _, found := processed[gr.ID]; found {
- result.Shared = append(result.Shared, gr)
- } else {
- result.After = append(result.After, gr)
- }
- processed[gr.ID] = true
- }
- for _, gr := range before {
- if done := processed[gr.ID]; !done {
- result.Before = append(result.Before, gr)
- }
- }
- return result
-}
-
-// TODO: do we want to allow contraction of stacks before comparison?
-func (s *Summary) addGoroutine(gr Goroutine) {
- index := sort.Search(len(s.Calls), func(i int) bool {
- return !s.Calls[i].Stack.less(gr.Stack)
- })
- if index >= len(s.Calls) || !s.Calls[index].Stack.equal(gr.Stack) {
- // insert new stack, first increase the length
- s.Calls = append(s.Calls, Call{})
- // move the top part upward to make space
- copy(s.Calls[index+1:], s.Calls[index:])
- // insert the new call
- s.Calls[index] = Call{
- Stack: gr.Stack,
- }
- }
- // merge the goroutine into the matched call
- s.Calls[index].merge(gr)
-}
-
-//TODO: do we want other grouping strategies?
-func (c *Call) merge(gr Goroutine) {
- for i := range c.Groups {
- canditate := &c.Groups[i]
- if canditate.State == gr.State {
- canditate.Goroutines = append(canditate.Goroutines, gr)
- return
- }
- }
- c.Groups = append(c.Groups, Group{
- State: gr.State,
- Goroutines: []Goroutine{gr},
- })
-}