github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/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 "cmd/compile/internal/types" 13 "cmd/internal/src" 14 "fmt" 15 "io" 16 "os" 17 "reflect" 18 "regexp" 19 "unicode" 20 "unicode/utf8" 21 ) 22 23 // dump is like fdump but prints to stderr. 24 func dump(root interface{}, filter string, depth int) { 25 fdump(os.Stderr, root, filter, depth) 26 } 27 28 // fdump 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 fdump(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 // special cases 144 switch v := x.Interface().(type) { 145 case Nodes: 146 // unpack Nodes since reflect cannot look inside 147 // due to the unexported field in its struct 148 x = reflect.ValueOf(v.Slice()) 149 150 case src.XPos: 151 p.printf("%s", linestr(v)) 152 return 153 154 case *types.Node: 155 x = reflect.ValueOf(asNode(v)) 156 } 157 158 switch x.Kind() { 159 case reflect.String: 160 p.printf("%q", x.Interface()) // print strings in quotes 161 162 case reflect.Interface: 163 if x.IsNil() { 164 p.printf("nil") 165 return 166 } 167 p.dump(x.Elem(), depth-1) 168 169 case reflect.Ptr: 170 if x.IsNil() { 171 p.printf("nil") 172 return 173 } 174 175 p.printf("*") 176 ptr := x.Pointer() 177 if line, exists := p.ptrmap[ptr]; exists { 178 p.printf("(@%d)", line) 179 return 180 } 181 p.ptrmap[ptr] = p.line 182 p.dump(x.Elem(), depth) // don't count pointer indirection towards depth 183 184 case reflect.Slice: 185 if x.IsNil() { 186 p.printf("nil") 187 return 188 } 189 p.printf("%s (%d entries) {", x.Type(), x.Len()) 190 if x.Len() > 0 { 191 p.indent++ 192 p.printf("\n") 193 for i, n := 0, x.Len(); i < n; i++ { 194 p.printf("%d: ", i) 195 p.dump(x.Index(i), depth-1) 196 p.printf("\n") 197 } 198 p.indent-- 199 } 200 p.printf("}") 201 202 case reflect.Struct: 203 typ := x.Type() 204 205 isNode := false 206 if n, ok := x.Interface().(Node); ok { 207 isNode = true 208 p.printf("%s %s {", n.Op.String(), p.addr(x)) 209 } else { 210 p.printf("%s {", typ) 211 } 212 p.indent++ 213 214 first := true 215 omitted := false 216 for i, n := 0, typ.NumField(); i < n; i++ { 217 // Exclude non-exported fields because their 218 // values cannot be accessed via reflection. 219 if name := typ.Field(i).Name; isExported(name) { 220 if !p.fieldrx.MatchString(name) { 221 omitted = true 222 continue // field name not selected by filter 223 } 224 225 // special cases 226 if isNode && name == "Op" { 227 omitted = true 228 continue // Op field already printed for Nodes 229 } 230 x := x.Field(i) 231 if isZeroVal(x) { 232 omitted = true 233 continue // exclude zero-valued fields 234 } 235 if n, ok := x.Interface().(Nodes); ok && n.Len() == 0 { 236 omitted = true 237 continue // exclude empty Nodes slices 238 } 239 240 if first { 241 p.printf("\n") 242 first = false 243 } 244 p.printf("%s: ", name) 245 p.dump(x, depth-1) 246 p.printf("\n") 247 } 248 } 249 if omitted { 250 p.printf("…\n") 251 } 252 253 p.indent-- 254 p.printf("}") 255 256 default: 257 p.printf("%v", x.Interface()) 258 } 259 } 260 261 func isZeroVal(x reflect.Value) bool { 262 switch x.Kind() { 263 case reflect.Bool: 264 return !x.Bool() 265 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 266 return x.Int() == 0 267 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 268 return x.Uint() == 0 269 case reflect.String: 270 return x.String() == "" 271 case reflect.Interface, reflect.Ptr, reflect.Slice: 272 return x.IsNil() 273 } 274 return false 275 } 276 277 func isExported(name string) bool { 278 ch, _ := utf8.DecodeRuneInString(name) 279 return unicode.IsUpper(ch) 280 } 281 282 func commonPrefixLen(a, b string) (i int) { 283 for i < len(a) && i < len(b) && a[i] == b[i] { 284 i++ 285 } 286 return 287 }