@@ 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("}")
}