github.com/aclements/go-misc@v0.0.0-20240129233631-2f6ede80790c/rtcheck/debug.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 ) 12 13 // A DebugTree captures a hierarchical debug trace. It's useful for 14 // visualizing the execution of recursive functions. 15 type DebugTree struct { 16 cur *debugTreeNode 17 roots []*debugTreeNode 18 19 nextEdge string 20 } 21 22 type debugTreeNode struct { 23 label string 24 parent *debugTreeNode 25 edges []string 26 children []*debugTreeNode 27 } 28 29 func (t *DebugTree) Push(label string) { 30 node := &debugTreeNode{label: label, parent: t.cur} 31 if t.cur == nil { 32 t.roots = append(t.roots, node) 33 } else { 34 t.cur.edges = append(t.cur.edges, t.nextEdge) 35 t.cur.children = append(t.cur.children, node) 36 } 37 t.cur = node 38 t.nextEdge = "" 39 } 40 41 func (t *DebugTree) Pushf(format string, args ...interface{}) { 42 t.Push(fmt.Sprintf(format, args...)) 43 } 44 45 func (t *DebugTree) Append(label string) { 46 t.cur.label += label 47 } 48 49 func (t *DebugTree) Appendf(format string, args ...interface{}) { 50 t.Append(fmt.Sprintf(format, args...)) 51 } 52 53 func (t *DebugTree) Pop() { 54 if t.cur == nil { 55 panic("unbalanced Push/Pop") 56 } 57 t.cur = t.cur.parent 58 t.nextEdge = "" 59 } 60 61 func (t *DebugTree) Leaf(label string) { 62 t.Push(label) 63 t.Pop() 64 } 65 66 func (t *DebugTree) Leaff(format string, args ...interface{}) { 67 t.Leaf(fmt.Sprintf(format, args...)) 68 } 69 70 func (t *DebugTree) SetEdge(label string) { 71 t.nextEdge = label 72 } 73 74 func (t *DebugTree) WriteToDot(w io.Writer) { 75 id := func(n *debugTreeNode) string { 76 return fmt.Sprintf("n%p", n) 77 } 78 79 var rec func(n *debugTreeNode) 80 rec = func(n *debugTreeNode) { 81 nid := id(n) 82 fmt.Fprintf(w, "%s [label=%q];\n", nid, n.label) 83 for i, child := range n.children { 84 fmt.Fprintf(w, "%s -> %s", nid, id(child)) 85 if n.edges[i] != "" { 86 fmt.Fprintf(w, " [label=%q]", n.edges[i]) 87 } 88 fmt.Fprint(w, ";\n") 89 rec(child) 90 } 91 } 92 93 fmt.Fprint(w, "digraph debug {\n") 94 for _, root := range t.roots { 95 rec(root) 96 } 97 fmt.Fprint(w, "}\n") 98 } 99 100 type IndentWriter struct { 101 W io.Writer 102 Indent []byte 103 inLine bool 104 } 105 106 func (w *IndentWriter) Write(p []byte) (n int, err error) { 107 total := 0 108 for len(p) > 0 { 109 if !w.inLine { 110 _, err := w.W.Write(w.Indent) 111 if err != nil { 112 return total, err 113 } 114 w.inLine = true 115 } 116 117 next := bytes.IndexByte(p, '\n') 118 if next < 0 { 119 n, err := w.W.Write(p) 120 total += n 121 return total, err 122 } 123 line, rest := p[:next+1], p[next+1:] 124 n, err := w.W.Write(line) 125 total += n 126 if n < len(line) || err != nil { 127 return total, err 128 } 129 w.inLine = false 130 p = rest 131 } 132 return total, nil 133 }