dump.go (13794B)
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 }