dump.go raw

   1  /*
   2   * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
   3   *
   4   * Permission to use, copy, modify, and distribute this software for any
   5   * purpose with or without fee is hereby granted, provided that the above
   6   * copyright notice and this permission notice appear in all copies.
   7   *
   8   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15   */
  16  
  17  package spew
  18  
  19  import (
  20  	"bytes"
  21  	"encoding/hex"
  22  	"fmt"
  23  	"io"
  24  	"os"
  25  	"reflect"
  26  	"regexp"
  27  	"strconv"
  28  	"strings"
  29  )
  30  
  31  var (
  32  	// uint8Type is a reflect.Type representing a uint8.  It is used to
  33  	// convert cgo types to uint8 slices for hexdumping.
  34  	uint8Type = reflect.TypeOf(uint8(0))
  35  
  36  	// cCharRE is a regular expression that matches a cgo char.
  37  	// It is used to detect character arrays to hexdump them.
  38  	cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
  39  
  40  	// cUnsignedCharRE is a regular expression that matches a cgo unsigned
  41  	// char.  It is used to detect unsigned character arrays to hexdump
  42  	// them.
  43  	cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
  44  
  45  	// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
  46  	// It is used to detect uint8_t arrays to hexdump them.
  47  	cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
  48  )
  49  
  50  // dumpState contains information about the state of a dump operation.
  51  type dumpState struct {
  52  	w                io.Writer
  53  	depth            int
  54  	pointers         map[uintptr]int
  55  	ignoreNextType   bool
  56  	ignoreNextIndent bool
  57  	cs               *ConfigState
  58  }
  59  
  60  // indent performs indentation according to the depth level and cs.Indent
  61  // option.
  62  func (d *dumpState) indent() {
  63  	if d.ignoreNextIndent {
  64  		d.ignoreNextIndent = false
  65  		return
  66  	}
  67  	d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
  68  }
  69  
  70  // unpackValue returns values inside of non-nil interfaces when possible.
  71  // This is useful for data types like structs, arrays, slices, and maps which
  72  // can contain varying types packed inside an interface.
  73  func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
  74  	if v.Kind() == reflect.Interface && !v.IsNil() {
  75  		v = v.Elem()
  76  	}
  77  	return v
  78  }
  79  
  80  // dumpPtr handles formatting of pointers by indirecting them as necessary.
  81  func (d *dumpState) dumpPtr(v reflect.Value) {
  82  	// Remove pointers at or below the current depth from map used to detect
  83  	// circular refs.
  84  	for k, depth := range d.pointers {
  85  		if depth >= d.depth {
  86  			delete(d.pointers, k)
  87  		}
  88  	}
  89  
  90  	// Keep list of all dereferenced pointers to show later.
  91  	pointerChain := make([]uintptr, 0)
  92  
  93  	// Figure out how many levels of indirection there are by dereferencing
  94  	// pointers and unpacking interfaces down the chain while detecting circular
  95  	// references.
  96  	nilFound := false
  97  	cycleFound := false
  98  	indirects := 0
  99  	ve := v
 100  	for ve.Kind() == reflect.Ptr {
 101  		if ve.IsNil() {
 102  			nilFound = true
 103  			break
 104  		}
 105  		indirects++
 106  		addr := ve.Pointer()
 107  		pointerChain = append(pointerChain, addr)
 108  		if pd, ok := d.pointers[addr]; ok && pd < d.depth {
 109  			cycleFound = true
 110  			indirects--
 111  			break
 112  		}
 113  		d.pointers[addr] = d.depth
 114  
 115  		ve = ve.Elem()
 116  		if ve.Kind() == reflect.Interface {
 117  			if ve.IsNil() {
 118  				nilFound = true
 119  				break
 120  			}
 121  			ve = ve.Elem()
 122  		}
 123  	}
 124  
 125  	// Display type information.
 126  	d.w.Write(openParenBytes)
 127  	d.w.Write(bytes.Repeat(asteriskBytes, indirects))
 128  	d.w.Write([]byte(ve.Type().String()))
 129  	d.w.Write(closeParenBytes)
 130  
 131  	// Display pointer information.
 132  	if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
 133  		d.w.Write(openParenBytes)
 134  		for i, addr := range pointerChain {
 135  			if i > 0 {
 136  				d.w.Write(pointerChainBytes)
 137  			}
 138  			printHexPtr(d.w, addr)
 139  		}
 140  		d.w.Write(closeParenBytes)
 141  	}
 142  
 143  	// Display dereferenced value.
 144  	d.w.Write(openParenBytes)
 145  	switch {
 146  	case nilFound:
 147  		d.w.Write(nilAngleBytes)
 148  
 149  	case cycleFound:
 150  		d.w.Write(circularBytes)
 151  
 152  	default:
 153  		d.ignoreNextType = true
 154  		d.dump(ve)
 155  	}
 156  	d.w.Write(closeParenBytes)
 157  }
 158  
 159  // dumpSlice handles formatting of arrays and slices.  Byte (uint8 under
 160  // reflection) arrays and slices are dumped in hexdump -C fashion.
 161  func (d *dumpState) dumpSlice(v reflect.Value) {
 162  	// Determine whether this type should be hex dumped or not.  Also,
 163  	// for types which should be hexdumped, try to use the underlying data
 164  	// first, then fall back to trying to convert them to a uint8 slice.
 165  	var buf []uint8
 166  	doConvert := false
 167  	doHexDump := false
 168  	numEntries := v.Len()
 169  	if numEntries > 0 {
 170  		vt := v.Index(0).Type()
 171  		vts := vt.String()
 172  		switch {
 173  		// C types that need to be converted.
 174  		case cCharRE.MatchString(vts):
 175  			fallthrough
 176  		case cUnsignedCharRE.MatchString(vts):
 177  			fallthrough
 178  		case cUint8tCharRE.MatchString(vts):
 179  			doConvert = true
 180  
 181  		// Try to use existing uint8 slices and fall back to converting
 182  		// and copying if that fails.
 183  		case vt.Kind() == reflect.Uint8:
 184  			// We need an addressable interface to convert the type
 185  			// to a byte slice.  However, the reflect package won't
 186  			// give us an interface on certain things like
 187  			// unexported struct fields in order to enforce
 188  			// visibility rules.  We use unsafe, when available, to
 189  			// bypass these restrictions since this package does not
 190  			// mutate the values.
 191  			vs := v
 192  			if !vs.CanInterface() || !vs.CanAddr() {
 193  				vs = unsafeReflectValue(vs)
 194  			}
 195  			if !UnsafeDisabled {
 196  				vs = vs.Slice(0, numEntries)
 197  
 198  				// Use the existing uint8 slice if it can be
 199  				// type asserted.
 200  				iface := vs.Interface()
 201  				if slice, ok := iface.([]uint8); ok {
 202  					buf = slice
 203  					doHexDump = true
 204  					break
 205  				}
 206  			}
 207  
 208  			// The underlying data needs to be converted if it can't
 209  			// be type asserted to a uint8 slice.
 210  			doConvert = true
 211  		}
 212  
 213  		// Copy and convert the underlying type if needed.
 214  		if doConvert && vt.ConvertibleTo(uint8Type) {
 215  			// Convert and copy each element into a uint8 byte
 216  			// slice.
 217  			buf = make([]uint8, numEntries)
 218  			for i := 0; i < numEntries; i++ {
 219  				vv := v.Index(i)
 220  				buf[i] = uint8(vv.Convert(uint8Type).Uint())
 221  			}
 222  			doHexDump = true
 223  		}
 224  	}
 225  
 226  	// Hexdump the entire slice as needed.
 227  	if doHexDump {
 228  		indent := strings.Repeat(d.cs.Indent, d.depth)
 229  		str := indent + hex.Dump(buf)
 230  		str = strings.Replace(str, "\n", "\n"+indent, -1)
 231  		str = strings.TrimRight(str, d.cs.Indent)
 232  		d.w.Write([]byte(str))
 233  		return
 234  	}
 235  
 236  	// Recursively call dump for each item.
 237  	for i := 0; i < numEntries; i++ {
 238  		d.dump(d.unpackValue(v.Index(i)))
 239  		if i < (numEntries - 1) {
 240  			d.w.Write(commaNewlineBytes)
 241  		} else {
 242  			d.w.Write(newlineBytes)
 243  		}
 244  	}
 245  }
 246  
 247  // dump is the main workhorse for dumping a value.  It uses the passed reflect
 248  // value to figure out what kind of object we are dealing with and formats it
 249  // appropriately.  It is a recursive function, however circular data structures
 250  // are detected and handled properly.
 251  func (d *dumpState) dump(v reflect.Value) {
 252  	// Handle invalid reflect values immediately.
 253  	kind := v.Kind()
 254  	if kind == reflect.Invalid {
 255  		d.w.Write(invalidAngleBytes)
 256  		return
 257  	}
 258  
 259  	// Handle pointers specially.
 260  	if kind == reflect.Ptr {
 261  		d.indent()
 262  		d.dumpPtr(v)
 263  		return
 264  	}
 265  
 266  	// Print type information unless already handled elsewhere.
 267  	if !d.ignoreNextType {
 268  		d.indent()
 269  		d.w.Write(openParenBytes)
 270  		d.w.Write([]byte(v.Type().String()))
 271  		d.w.Write(closeParenBytes)
 272  		d.w.Write(spaceBytes)
 273  	}
 274  	d.ignoreNextType = false
 275  
 276  	// Display length and capacity if the built-in len and cap functions
 277  	// work with the value's kind and the len/cap itself is non-zero.
 278  	valueLen, valueCap := 0, 0
 279  	switch v.Kind() {
 280  	case reflect.Array, reflect.Slice, reflect.Chan:
 281  		valueLen, valueCap = v.Len(), v.Cap()
 282  	case reflect.Map, reflect.String:
 283  		valueLen = v.Len()
 284  	}
 285  	if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
 286  		d.w.Write(openParenBytes)
 287  		if valueLen != 0 {
 288  			d.w.Write(lenEqualsBytes)
 289  			printInt(d.w, int64(valueLen), 10)
 290  		}
 291  		if !d.cs.DisableCapacities && valueCap != 0 {
 292  			if valueLen != 0 {
 293  				d.w.Write(spaceBytes)
 294  			}
 295  			d.w.Write(capEqualsBytes)
 296  			printInt(d.w, int64(valueCap), 10)
 297  		}
 298  		d.w.Write(closeParenBytes)
 299  		d.w.Write(spaceBytes)
 300  	}
 301  
 302  	// Call Stringer/error interfaces if they exist and the handle methods flag
 303  	// is enabled
 304  	if !d.cs.DisableMethods {
 305  		if (kind != reflect.Invalid) && (kind != reflect.Interface) {
 306  			if handled := handleMethods(d.cs, d.w, v); handled {
 307  				return
 308  			}
 309  		}
 310  	}
 311  
 312  	switch kind {
 313  	case reflect.Invalid:
 314  		// Do nothing.  We should never get here since invalid has already
 315  		// been handled above.
 316  
 317  	case reflect.Bool:
 318  		printBool(d.w, v.Bool())
 319  
 320  	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
 321  		printInt(d.w, v.Int(), 10)
 322  
 323  	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
 324  		printUint(d.w, v.Uint(), 10)
 325  
 326  	case reflect.Float32:
 327  		printFloat(d.w, v.Float(), 32)
 328  
 329  	case reflect.Float64:
 330  		printFloat(d.w, v.Float(), 64)
 331  
 332  	case reflect.Complex64:
 333  		printComplex(d.w, v.Complex(), 32)
 334  
 335  	case reflect.Complex128:
 336  		printComplex(d.w, v.Complex(), 64)
 337  
 338  	case reflect.Slice:
 339  		if v.IsNil() {
 340  			d.w.Write(nilAngleBytes)
 341  			break
 342  		}
 343  		fallthrough
 344  
 345  	case reflect.Array:
 346  		d.w.Write(openBraceNewlineBytes)
 347  		d.depth++
 348  		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
 349  			d.indent()
 350  			d.w.Write(maxNewlineBytes)
 351  		} else {
 352  			d.dumpSlice(v)
 353  		}
 354  		d.depth--
 355  		d.indent()
 356  		d.w.Write(closeBraceBytes)
 357  
 358  	case reflect.String:
 359  		d.w.Write([]byte(strconv.Quote(v.String())))
 360  
 361  	case reflect.Interface:
 362  		// The only time we should get here is for nil interfaces due to
 363  		// unpackValue calls.
 364  		if v.IsNil() {
 365  			d.w.Write(nilAngleBytes)
 366  		}
 367  
 368  	case reflect.Ptr:
 369  		// Do nothing.  We should never get here since pointers have already
 370  		// been handled above.
 371  
 372  	case reflect.Map:
 373  		// nil maps should be indicated as different than empty maps
 374  		if v.IsNil() {
 375  			d.w.Write(nilAngleBytes)
 376  			break
 377  		}
 378  
 379  		d.w.Write(openBraceNewlineBytes)
 380  		d.depth++
 381  		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
 382  			d.indent()
 383  			d.w.Write(maxNewlineBytes)
 384  		} else {
 385  			numEntries := v.Len()
 386  			keys := v.MapKeys()
 387  			if d.cs.SortKeys {
 388  				sortValues(keys, d.cs)
 389  			}
 390  			for i, key := range keys {
 391  				d.dump(d.unpackValue(key))
 392  				d.w.Write(colonSpaceBytes)
 393  				d.ignoreNextIndent = true
 394  				d.dump(d.unpackValue(v.MapIndex(key)))
 395  				if i < (numEntries - 1) {
 396  					d.w.Write(commaNewlineBytes)
 397  				} else {
 398  					d.w.Write(newlineBytes)
 399  				}
 400  			}
 401  		}
 402  		d.depth--
 403  		d.indent()
 404  		d.w.Write(closeBraceBytes)
 405  
 406  	case reflect.Struct:
 407  		d.w.Write(openBraceNewlineBytes)
 408  		d.depth++
 409  		if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
 410  			d.indent()
 411  			d.w.Write(maxNewlineBytes)
 412  		} else {
 413  			vt := v.Type()
 414  			numFields := v.NumField()
 415  			for i := 0; i < numFields; i++ {
 416  				d.indent()
 417  				vtf := vt.Field(i)
 418  				d.w.Write([]byte(vtf.Name))
 419  				d.w.Write(colonSpaceBytes)
 420  				d.ignoreNextIndent = true
 421  				d.dump(d.unpackValue(v.Field(i)))
 422  				if i < (numFields - 1) {
 423  					d.w.Write(commaNewlineBytes)
 424  				} else {
 425  					d.w.Write(newlineBytes)
 426  				}
 427  			}
 428  		}
 429  		d.depth--
 430  		d.indent()
 431  		d.w.Write(closeBraceBytes)
 432  
 433  	case reflect.Uintptr:
 434  		printHexPtr(d.w, uintptr(v.Uint()))
 435  
 436  	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
 437  		printHexPtr(d.w, v.Pointer())
 438  
 439  	// There were not any other types at the time this code was written, but
 440  	// fall back to letting the default fmt package handle it in case any new
 441  	// types are added.
 442  	default:
 443  		if v.CanInterface() {
 444  			fmt.Fprintf(d.w, "%v", v.Interface())
 445  		} else {
 446  			fmt.Fprintf(d.w, "%v", v.String())
 447  		}
 448  	}
 449  }
 450  
 451  // fdump is a helper function to consolidate the logic from the various public
 452  // methods which take varying writers and config states.
 453  func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
 454  	for _, arg := range a {
 455  		if arg == nil {
 456  			w.Write(interfaceBytes)
 457  			w.Write(spaceBytes)
 458  			w.Write(nilAngleBytes)
 459  			w.Write(newlineBytes)
 460  			continue
 461  		}
 462  
 463  		d := dumpState{w: w, cs: cs}
 464  		d.pointers = make(map[uintptr]int)
 465  		d.dump(reflect.ValueOf(arg))
 466  		d.w.Write(newlineBytes)
 467  	}
 468  }
 469  
 470  // Fdump formats and displays the passed arguments to io.Writer w.  It formats
 471  // exactly the same as Dump.
 472  func Fdump(w io.Writer, a ...interface{}) {
 473  	fdump(&Config, w, a...)
 474  }
 475  
 476  // Sdump returns a string with the passed arguments formatted exactly the same
 477  // as Dump.
 478  func Sdump(a ...interface{}) string {
 479  	var buf bytes.Buffer
 480  	fdump(&Config, &buf, a...)
 481  	return buf.String()
 482  }
 483  
 484  /*
 485  Dump displays the passed parameters to standard out with newlines, customizable
 486  indentation, and additional debug information such as complete types and all
 487  pointer addresses used to indirect to the final value.  It provides the
 488  following features over the built-in printing facilities provided by the fmt
 489  package:
 490  
 491  	* Pointers are dereferenced and followed
 492  	* Circular data structures are detected and handled properly
 493  	* Custom Stringer/error interfaces are optionally invoked, including
 494  	  on unexported types
 495  	* Custom types which only implement the Stringer/error interfaces via
 496  	  a pointer receiver are optionally invoked when passing non-pointer
 497  	  variables
 498  	* Byte arrays and slices are dumped like the hexdump -C command which
 499  	  includes offsets, byte values in hex, and ASCII output
 500  
 501  The configuration options are controlled by an exported package global,
 502  spew.Config.  See ConfigState for options documentation.
 503  
 504  See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
 505  get the formatted result as a string.
 506  */
 507  func Dump(a ...interface{}) {
 508  	fdump(&Config, os.Stdout, a...)
 509  }
 510