dump.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. /*
  2. * Copyright (c) 2013 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. package spew
  17. import (
  18. "bytes"
  19. "encoding/hex"
  20. "fmt"
  21. "io"
  22. "os"
  23. "reflect"
  24. "strconv"
  25. "strings"
  26. )
  27. // dumpState contains information about the state of a dump operation.
  28. type dumpState struct {
  29. w io.Writer
  30. depth int
  31. pointers map[uintptr]int
  32. ignoreNextType bool
  33. ignoreNextIndent bool
  34. cs *ConfigState
  35. }
  36. // indent performs indentation according to the depth level and cs.Indent
  37. // option.
  38. func (d *dumpState) indent() {
  39. if d.ignoreNextIndent {
  40. d.ignoreNextIndent = false
  41. return
  42. }
  43. d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
  44. }
  45. // unpackValue returns values inside of non-nil interfaces when possible.
  46. // This is useful for data types like structs, arrays, slices, and maps which
  47. // can contain varying types packed inside an interface.
  48. func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
  49. if v.Kind() == reflect.Interface && !v.IsNil() {
  50. v = v.Elem()
  51. }
  52. return v
  53. }
  54. // dumpPtr handles formatting of pointers by indirecting them as necessary.
  55. func (d *dumpState) dumpPtr(v reflect.Value) {
  56. // Remove pointers at or below the current depth from map used to detect
  57. // circular refs.
  58. for k, depth := range d.pointers {
  59. if depth >= d.depth {
  60. delete(d.pointers, k)
  61. }
  62. }
  63. // Keep list of all dereferenced pointers to show later.
  64. pointerChain := make([]uintptr, 0)
  65. // Figure out how many levels of indirection there are by dereferencing
  66. // pointers and unpacking interfaces down the chain while detecting circular
  67. // references.
  68. nilFound := false
  69. cycleFound := false
  70. indirects := 0
  71. ve := v
  72. for ve.Kind() == reflect.Ptr {
  73. if ve.IsNil() {
  74. nilFound = true
  75. break
  76. }
  77. indirects++
  78. addr := ve.Pointer()
  79. pointerChain = append(pointerChain, addr)
  80. if pd, ok := d.pointers[addr]; ok && pd < d.depth {
  81. cycleFound = true
  82. indirects--
  83. break
  84. }
  85. d.pointers[addr] = d.depth
  86. ve = ve.Elem()
  87. if ve.Kind() == reflect.Interface {
  88. if ve.IsNil() {
  89. nilFound = true
  90. break
  91. }
  92. ve = ve.Elem()
  93. }
  94. }
  95. // Display type information.
  96. d.w.Write(openParenBytes)
  97. d.w.Write(bytes.Repeat(asteriskBytes, indirects))
  98. d.w.Write([]byte(ve.Type().String()))
  99. d.w.Write(closeParenBytes)
  100. // Display pointer information.
  101. if len(pointerChain) > 0 {
  102. d.w.Write(openParenBytes)
  103. for i, addr := range pointerChain {
  104. if i > 0 {
  105. d.w.Write(pointerChainBytes)
  106. }
  107. printHexPtr(d.w, addr)
  108. }
  109. d.w.Write(closeParenBytes)
  110. }
  111. // Display dereferenced value.
  112. d.w.Write(openParenBytes)
  113. switch {
  114. case nilFound == true:
  115. d.w.Write(nilAngleBytes)
  116. case cycleFound == true:
  117. d.w.Write(circularBytes)
  118. default:
  119. d.ignoreNextType = true
  120. d.dump(ve)
  121. }
  122. d.w.Write(closeParenBytes)
  123. }
  124. // dumpSlice handles formatting of arrays and slices. Byte (uint8 under
  125. // reflection) arrays and slices are dumped in hexdump -C fashion.
  126. func (d *dumpState) dumpSlice(v reflect.Value) {
  127. // Handle byte (uint8 under reflection) arrays and slices uniquely.
  128. numEntries := v.Len()
  129. if (numEntries > 0) && (v.Index(0).Kind() == reflect.Uint8) {
  130. // We need an addressable interface to convert the type back into a byte
  131. // slice. However, the reflect package won't give us an interface on
  132. // certain things like unexported struct fields in order to enforce
  133. // visibility rules. We use unsafe to bypass these restrictions since
  134. // this package does not mutate the values.
  135. vs := v
  136. if !vs.CanInterface() || !vs.CanAddr() {
  137. vs = unsafeReflectValue(vs)
  138. }
  139. vs = vs.Slice(0, numEntries)
  140. // Type assert a uint8 slice and hexdump it. Also fix indentation
  141. // based on the depth.
  142. iface := vs.Interface()
  143. if buf, ok := iface.([]uint8); ok {
  144. indent := strings.Repeat(d.cs.Indent, d.depth)
  145. str := indent + hex.Dump(buf)
  146. str = strings.Replace(str, "\n", "\n"+indent, -1)
  147. str = strings.TrimRight(str, d.cs.Indent)
  148. d.w.Write([]byte(str))
  149. return
  150. }
  151. // We shouldn't ever get here, but the return is intentionally in the
  152. // above if statement to ensure we fall through to normal behavior if
  153. // the type assertion fails for some reason.
  154. }
  155. // Recursively call dump for each item.
  156. for i := 0; i < numEntries; i++ {
  157. d.dump(d.unpackValue(v.Index(i)))
  158. if i < (numEntries - 1) {
  159. d.w.Write(commaNewlineBytes)
  160. } else {
  161. d.w.Write(newlineBytes)
  162. }
  163. }
  164. }
  165. // dump is the main workhorse for dumping a value. It uses the passed reflect
  166. // value to figure out what kind of object we are dealing with and formats it
  167. // appropriately. It is a recursive function, however circular data structures
  168. // are detected and handled properly.
  169. func (d *dumpState) dump(v reflect.Value) {
  170. // Handle invalid reflect values immediately.
  171. kind := v.Kind()
  172. if kind == reflect.Invalid {
  173. d.w.Write(invalidAngleBytes)
  174. return
  175. }
  176. // Handle pointers specially.
  177. if kind == reflect.Ptr {
  178. d.indent()
  179. d.dumpPtr(v)
  180. return
  181. }
  182. // Print type information unless already handled elsewhere.
  183. if !d.ignoreNextType {
  184. d.indent()
  185. d.w.Write(openParenBytes)
  186. d.w.Write([]byte(v.Type().String()))
  187. d.w.Write(closeParenBytes)
  188. d.w.Write(spaceBytes)
  189. }
  190. d.ignoreNextType = false
  191. // Call Stringer/error interfaces if they exist and the handle methods flag
  192. // is enabled
  193. if !d.cs.DisableMethods {
  194. if (kind != reflect.Invalid) && (kind != reflect.Interface) {
  195. if handled := handleMethods(d.cs, d.w, v); handled {
  196. return
  197. }
  198. }
  199. }
  200. switch kind {
  201. case reflect.Invalid:
  202. // Do nothing. We should never get here since invalid has already
  203. // been handled above.
  204. case reflect.Bool:
  205. printBool(d.w, v.Bool())
  206. case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
  207. printInt(d.w, v.Int(), 10)
  208. case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
  209. printUint(d.w, v.Uint(), 10)
  210. case reflect.Float32:
  211. printFloat(d.w, v.Float(), 32)
  212. case reflect.Float64:
  213. printFloat(d.w, v.Float(), 64)
  214. case reflect.Complex64:
  215. printComplex(d.w, v.Complex(), 32)
  216. case reflect.Complex128:
  217. printComplex(d.w, v.Complex(), 64)
  218. case reflect.Array, reflect.Slice:
  219. d.w.Write(openBraceNewlineBytes)
  220. d.depth++
  221. if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
  222. d.indent()
  223. d.w.Write(maxNewlineBytes)
  224. } else {
  225. d.dumpSlice(v)
  226. }
  227. d.depth--
  228. d.indent()
  229. d.w.Write(closeBraceBytes)
  230. case reflect.String:
  231. d.w.Write([]byte(strconv.Quote(v.String())))
  232. case reflect.Interface:
  233. // Do nothing. We should never get here due to unpackValue calls.
  234. case reflect.Ptr:
  235. // Do nothing. We should never get here since pointers have already
  236. // been handled above.
  237. case reflect.Map:
  238. d.w.Write(openBraceNewlineBytes)
  239. d.depth++
  240. if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
  241. d.indent()
  242. d.w.Write(maxNewlineBytes)
  243. } else {
  244. numEntries := v.Len()
  245. keys := v.MapKeys()
  246. for i, key := range keys {
  247. d.dump(d.unpackValue(key))
  248. d.w.Write(colonSpaceBytes)
  249. d.ignoreNextIndent = true
  250. d.dump(d.unpackValue(v.MapIndex(key)))
  251. if i < (numEntries - 1) {
  252. d.w.Write(commaNewlineBytes)
  253. } else {
  254. d.w.Write(newlineBytes)
  255. }
  256. }
  257. }
  258. d.depth--
  259. d.indent()
  260. d.w.Write(closeBraceBytes)
  261. case reflect.Struct:
  262. d.w.Write(openBraceNewlineBytes)
  263. d.depth++
  264. if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
  265. d.indent()
  266. d.w.Write(maxNewlineBytes)
  267. } else {
  268. vt := v.Type()
  269. numFields := v.NumField()
  270. for i := 0; i < numFields; i++ {
  271. d.indent()
  272. vtf := vt.Field(i)
  273. d.w.Write([]byte(vtf.Name))
  274. d.w.Write(colonSpaceBytes)
  275. d.ignoreNextIndent = true
  276. d.dump(d.unpackValue(v.Field(i)))
  277. if i < (numFields - 1) {
  278. d.w.Write(commaNewlineBytes)
  279. } else {
  280. d.w.Write(newlineBytes)
  281. }
  282. }
  283. }
  284. d.depth--
  285. d.indent()
  286. d.w.Write(closeBraceBytes)
  287. case reflect.Uintptr:
  288. printHexPtr(d.w, uintptr(v.Uint()))
  289. case reflect.UnsafePointer, reflect.Chan, reflect.Func:
  290. printHexPtr(d.w, v.Pointer())
  291. // There were not any other types at the time this code was written, but
  292. // fall back to letting the default fmt package handle it in case any new
  293. // types are added.
  294. default:
  295. if v.CanInterface() {
  296. fmt.Fprintf(d.w, "%v", v.Interface())
  297. } else {
  298. fmt.Fprintf(d.w, "%v", v.String())
  299. }
  300. }
  301. }
  302. // fdump is a helper function to consolidate the logic from the various public
  303. // methods which take varying writers and config states.
  304. func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
  305. for _, arg := range a {
  306. if arg == nil {
  307. w.Write(interfaceBytes)
  308. w.Write(spaceBytes)
  309. w.Write(nilAngleBytes)
  310. w.Write(newlineBytes)
  311. continue
  312. }
  313. d := dumpState{w: w, cs: cs}
  314. d.pointers = make(map[uintptr]int)
  315. d.dump(reflect.ValueOf(arg))
  316. d.w.Write(newlineBytes)
  317. }
  318. }
  319. // Fdump formats and displays the passed arguments to io.Writer w. It formats
  320. // exactly the same as Dump.
  321. func Fdump(w io.Writer, a ...interface{}) {
  322. fdump(&Config, w, a...)
  323. }
  324. /*
  325. Dump displays the passed parameters to standard out with newlines, customizable
  326. indentation, and additional debug information such as complete types and all
  327. pointer addresses used to indirect to the final value. It provides the
  328. following features over the built-in printing facilities provided by the fmt
  329. package:
  330. * Pointers are dereferenced and followed
  331. * Circular data structures are detected and handled properly
  332. * Custom Stringer/error interfaces are optionally invoked, including
  333. on unexported types
  334. * Custom types which only implement the Stringer/error interfaces via
  335. a pointer receiver are optionally invoked when passing non-pointer
  336. variables
  337. * Byte arrays and slices are dumped like the hexdump -C command which
  338. includes offsets, byte values in hex, and ASCII output
  339. The configuration options are controlled by an exported package global,
  340. spew.Config. See ConfigState for options documentation.
  341. See Fdump if you would prefer dumping to an arbitrary io.Writer.
  342. */
  343. func Dump(a ...interface{}) {
  344. fdump(&Config, os.Stdout, a...)
  345. }