~whereswaldon/stackgraph

31b9cdd55b5f126ea6c7a72020ec7a32b0fcd4da — Chris Waldon 4 months ago e2929aa
main: generate simple graphviz output

This commit teaches the program to write graphviz notation to stdout describing
a simplified graph of the goroutine relationships. Unfortunately, there are so
many goroutines that it's still too difficult to digest the results.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
1 files changed, 42 insertions(+), 1 deletions(-)

M main.go
M main.go => main.go +42 -1
@@ 5,10 5,13 @@ import (
	"io"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"
	"unicode"
	"unicode/utf8"

	"golang.org/x/exp/maps"
)

type tokenKind uint8


@@ 254,6 257,15 @@ type Info struct {
	CreateLine  string
}

func (i Info) Lines() string {
	var b strings.Builder
	for _, f := range i.Stack {
		b.WriteString(f.Line)
	}
	b.WriteString(i.CreateLine)
	return b.String()
}

type parser struct {
	lexer  lexer
	tokens []token


@@ 399,7 411,36 @@ func main() {
	}
	l := parser{}
	infos, err := l.parse(string(input))

	byStatus := make(map[string][]Info)
	for _, info := range infos {
		fmt.Printf("id: %d, status: %s, parent: %d, creator: %s[%s], stack: %v\n", info.GoroutineID, info.Status, info.ParentID, info.CreateFunc, info.CreateLine, info.Stack)
		byStatus[info.Status] = append(byStatus[info.Status], info)
	}
	// coalesce goroutines on the same line.
	byLines := make(map[string][]Info)
	idToGroup := make(map[int]int)
	for _, info := range infos {
		line := info.Stack[0].Line
		byLines[line] = append(byLines[line], info)
		idToGroup[info.GoroutineID] = byLines[line][0].GoroutineID
	}
	statuses := sort.StringSlice(maps.Keys(byStatus))
	fmt.Println("digraph {")
	for i, status := range statuses {
		_ = i
		infos := byStatus[status]

		for _, info := range infos {
			if group := idToGroup[info.GoroutineID]; group != info.GoroutineID {
				// Skip if it's not the first element in its group.
				continue
			}
			fmt.Printf("%d[label=\"%d %s\n%s\"]\n", info.GoroutineID, info.GoroutineID, info.Status, info.Stack[0].Call)
			if info.ParentID != 0 {
				// Create an edge to the first element in the parent's group, which may not be the parent itself.
				fmt.Printf("%d -> %d\n", idToGroup[info.ParentID], info.GoroutineID)
			}
		}
	}
	fmt.Println("}")
}