dump.go

  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
 17package spew
 18
 19import (
 20	"bytes"
 21	"encoding/hex"
 22	"fmt"
 23	"io"
 24	"os"
 25	"reflect"
 26	"regexp"
 27	"strconv"
 28	"strings"
 29)
 30
 31var (
 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.
 51type 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.
 62func (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.
 73func (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.
 81func (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.
161func (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.
251func (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.
453func 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.
472func 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.
478func Sdump(a ...interface{}) string {
479	var buf bytes.Buffer
480	fdump(&Config, &buf, a...)
481	return buf.String()
482}
483
484/*
485Dump displays the passed parameters to standard out with newlines, customizable
486indentation, and additional debug information such as complete types and all
487pointer addresses used to indirect to the final value.  It provides the
488following features over the built-in printing facilities provided by the fmt
489package:
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
501The configuration options are controlled by an exported package global,
502spew.Config.  See ConfigState for options documentation.
503
504See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
505get the formatted result as a string.
506*/
507func Dump(a ...interface{}) {
508	fdump(&Config, os.Stdout, a...)
509}