github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/ir/dump.go (about) 1 // Copyright 2018 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 // This file implements textual dumping of arbitrary data structures 6 // for debugging purposes. The code is customized for Node graphs 7 // and may be used for an alternative view of the node structure. 8 9 package ir 10 11 import ( 12 "fmt" 13 "io" 14 "os" 15 "reflect" 16 "regexp" 17 18 "github.com/go-asm/go/cmd/compile/base" 19 "github.com/go-asm/go/cmd/compile/types" 20 "github.com/go-asm/go/cmd/src" 21 ) 22 23 // DumpAny is like FDumpAny but prints to stderr. 24 func DumpAny(root interface{}, filter string, depth int) { 25 FDumpAny(os.Stderr, root, filter, depth) 26 } 27 28 // FDumpAny prints the structure of a rooted data structure 29 // to w by depth-first traversal of the data structure. 30 // 31 // The filter parameter is a regular expression. If it is 32 // non-empty, only struct fields whose names match filter 33 // are printed. 34 // 35 // The depth parameter controls how deep traversal recurses 36 // before it returns (higher value means greater depth). 37 // If an empty field filter is given, a good depth default value 38 // is 4. A negative depth means no depth limit, which may be fine 39 // for small data structures or if there is a non-empty filter. 40 // 41 // In the output, Node structs are identified by their Op name 42 // rather than their type; struct fields with zero values or 43 // non-matching field names are omitted, and "…" means recursion 44 // depth has been reached or struct fields have been omitted. 45 func FDumpAny(w io.Writer, root interface{}, filter string, depth int) { 46 if root == nil { 47 fmt.Fprintln(w, "nil") 48 return 49 } 50 51 if filter == "" { 52 filter = ".*" // default 53 } 54 55 p := dumper{ 56 output: w, 57 fieldrx: regexp.MustCompile(filter), 58 ptrmap: make(map[uintptr]int), 59 last: '\n', // force printing of line number on first line 60 } 61 62 p.dump(reflect.ValueOf(root), depth) 63 p.printf("\n") 64 } 65 66 type dumper struct { 67 output io.Writer 68 fieldrx *regexp.Regexp // field name filter 69 ptrmap map[uintptr]int // ptr -> dump line number 70 lastadr string // last address string printed (for shortening) 71 72 // output 73 indent int // current indentation level 74 last byte // last byte processed by Write 75 line int // current line number 76 } 77 78 var indentBytes = []byte(". ") 79 80 func (p *dumper) Write(data []byte) (n int, err error) { 81 var m int 82 for i, b := range data { 83 // invariant: data[0:n] has been written 84 if b == '\n' { 85 m, err = p.output.Write(data[n : i+1]) 86 n += m 87 if err != nil { 88 return 89 } 90 } else if p.last == '\n' { 91 p.line++ 92 _, err = fmt.Fprintf(p.output, "%6d ", p.line) 93 if err != nil { 94 return 95 } 96 for j := p.indent; j > 0; j-- { 97 _, err = p.output.Write(indentBytes) 98 if err != nil { 99 return 100 } 101 } 102 } 103 p.last = b 104 } 105 if len(data) > n { 106 m, err = p.output.Write(data[n:]) 107 n += m 108 } 109 return 110 } 111 112 // printf is a convenience wrapper. 113 func (p *dumper) printf(format string, args ...interface{}) { 114 if _, err := fmt.Fprintf(p, format, args...); err != nil { 115 panic(err) 116 } 117 } 118 119 // addr returns the (hexadecimal) address string of the object 120 // represented by x (or "?" if x is not addressable), with the 121 // common prefix between this and the prior address replaced by 122 // "0x…" to make it easier to visually match addresses. 123 func (p *dumper) addr(x reflect.Value) string { 124 if !x.CanAddr() { 125 return "?" 126 } 127 adr := fmt.Sprintf("%p", x.Addr().Interface()) 128 s := adr 129 if i := commonPrefixLen(p.lastadr, adr); i > 0 { 130 s = "0x…" + adr[i:] 131 } 132 p.lastadr = adr 133 return s 134 } 135 136 // dump prints the contents of x. 137 func (p *dumper) dump(x reflect.Value, depth int) { 138 if depth == 0 { 139 p.printf("…") 140 return 141 } 142 143 if pos, ok := x.Interface().(src.XPos); ok { 144 p.printf("%s", base.FmtPos(pos)) 145 return 146 } 147 148 switch x.Kind() { 149 case reflect.String: 150 p.printf("%q", x.Interface()) // print strings in quotes 151 152 case reflect.Interface: 153 if x.IsNil() { 154 p.printf("nil") 155 return 156 } 157 p.dump(x.Elem(), depth-1) 158 159 case reflect.Ptr: 160 if x.IsNil() { 161 p.printf("nil") 162 return 163 } 164 165 p.printf("*") 166 ptr := x.Pointer() 167 if line, exists := p.ptrmap[ptr]; exists { 168 p.printf("(@%d)", line) 169 return 170 } 171 p.ptrmap[ptr] = p.line 172 p.dump(x.Elem(), depth) // don't count pointer indirection towards depth 173 174 case reflect.Slice: 175 if x.IsNil() { 176 p.printf("nil") 177 return 178 } 179 p.printf("%s (%d entries) {", x.Type(), x.Len()) 180 if x.Len() > 0 { 181 p.indent++ 182 p.printf("\n") 183 for i, n := 0, x.Len(); i < n; i++ { 184 p.printf("%d: ", i) 185 p.dump(x.Index(i), depth-1) 186 p.printf("\n") 187 } 188 p.indent-- 189 } 190 p.printf("}") 191 192 case reflect.Struct: 193 typ := x.Type() 194 195 isNode := false 196 if n, ok := x.Interface().(Node); ok { 197 isNode = true 198 p.printf("%s %s {", n.Op().String(), p.addr(x)) 199 } else { 200 p.printf("%s {", typ) 201 } 202 p.indent++ 203 204 first := true 205 omitted := false 206 for i, n := 0, typ.NumField(); i < n; i++ { 207 // Exclude non-exported fields because their 208 // values cannot be accessed via reflection. 209 if name := typ.Field(i).Name; types.IsExported(name) { 210 if !p.fieldrx.MatchString(name) { 211 omitted = true 212 continue // field name not selected by filter 213 } 214 215 // special cases 216 if isNode && name == "Op" { 217 omitted = true 218 continue // Op field already printed for Nodes 219 } 220 x := x.Field(i) 221 if x.IsZero() { 222 omitted = true 223 continue // exclude zero-valued fields 224 } 225 if n, ok := x.Interface().(Nodes); ok && len(n) == 0 { 226 omitted = true 227 continue // exclude empty Nodes slices 228 } 229 230 if first { 231 p.printf("\n") 232 first = false 233 } 234 p.printf("%s: ", name) 235 p.dump(x, depth-1) 236 p.printf("\n") 237 } 238 } 239 if omitted { 240 p.printf("…\n") 241 } 242 243 p.indent-- 244 p.printf("}") 245 246 default: 247 p.printf("%v", x.Interface()) 248 } 249 } 250 251 func commonPrefixLen(a, b string) (i int) { 252 for i < len(a) && i < len(b) && a[i] == b[i] { 253 i++ 254 } 255 return 256 }