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  }