...

Source file src/pkg/cmd/compile/internal/gc/dump.go

     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	)
    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	}
   281	

View as plain text