dump.go 8.3 KB

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