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  }