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