source.go 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. // Copyright 2015 Marc-Antoine Ruel. All rights reserved.
  2. // Use of this source code is governed under the Apache License, Version 2.0
  3. // that can be found in the LICENSE file.
  4. // This file contains the code to process sources, to be able to deduct the
  5. // original types.
  6. package stack
  7. import (
  8. "bytes"
  9. "fmt"
  10. "go/ast"
  11. "go/parser"
  12. "go/token"
  13. "io/ioutil"
  14. "log"
  15. "math"
  16. "strings"
  17. )
  18. // cache is a cache of sources on the file system.
  19. type cache struct {
  20. files map[string][]byte
  21. parsed map[string]*parsedFile
  22. }
  23. // Augment processes source files to improve calls to be more descriptive.
  24. //
  25. // It modifies goroutines in place. It requires calling ParseDump() with
  26. // guesspaths set to true to work properly.
  27. func Augment(goroutines []*Goroutine) {
  28. c := &cache{}
  29. for _, g := range goroutines {
  30. c.augmentGoroutine(g)
  31. }
  32. }
  33. // augmentGoroutine processes source files to improve call to be more
  34. // descriptive.
  35. //
  36. // It modifies the routine.
  37. func (c *cache) augmentGoroutine(goroutine *Goroutine) {
  38. if c.files == nil {
  39. c.files = map[string][]byte{}
  40. }
  41. if c.parsed == nil {
  42. c.parsed = map[string]*parsedFile{}
  43. }
  44. // For each call site, look at the next call and populate it. Then we can
  45. // walk back and reformat things.
  46. for i := range goroutine.Stack.Calls {
  47. c.load(goroutine.Stack.Calls[i].LocalSrcPath)
  48. }
  49. // Once all loaded, we can look at the next call when available.
  50. for i := 0; i < len(goroutine.Stack.Calls)-1; i++ {
  51. // Get the AST from the previous call and process the call line with it.
  52. if f := c.getFuncAST(&goroutine.Stack.Calls[i]); f != nil {
  53. processCall(&goroutine.Stack.Calls[i], f)
  54. }
  55. }
  56. }
  57. // Private stuff.
  58. // load loads a source file and parses the AST tree. Failures are ignored.
  59. func (c *cache) load(fileName string) {
  60. if _, ok := c.parsed[fileName]; ok {
  61. return
  62. }
  63. c.parsed[fileName] = nil
  64. if !strings.HasSuffix(fileName, ".go") {
  65. // Ignore C and assembly.
  66. c.files[fileName] = nil
  67. return
  68. }
  69. log.Printf("load(%s)", fileName)
  70. if _, ok := c.files[fileName]; !ok {
  71. var err error
  72. if c.files[fileName], err = ioutil.ReadFile(fileName); err != nil {
  73. log.Printf("Failed to read %s: %s", fileName, err)
  74. c.files[fileName] = nil
  75. return
  76. }
  77. }
  78. fset := token.NewFileSet()
  79. src := c.files[fileName]
  80. parsed, err := parser.ParseFile(fset, fileName, src, 0)
  81. if err != nil {
  82. log.Printf("Failed to parse %s: %s", fileName, err)
  83. return
  84. }
  85. // Convert the line number into raw file offset.
  86. offsets := []int{0, 0}
  87. start := 0
  88. for l := 1; start < len(src); l++ {
  89. start += bytes.IndexByte(src[start:], '\n') + 1
  90. offsets = append(offsets, start)
  91. }
  92. c.parsed[fileName] = &parsedFile{offsets, parsed}
  93. }
  94. func (c *cache) getFuncAST(call *Call) *ast.FuncDecl {
  95. if p := c.parsed[call.LocalSrcPath]; p != nil {
  96. return p.getFuncAST(call.Func.Name(), call.Line)
  97. }
  98. return nil
  99. }
  100. type parsedFile struct {
  101. lineToByteOffset []int
  102. parsed *ast.File
  103. }
  104. // getFuncAST gets the callee site function AST representation for the code
  105. // inside the function f at line l.
  106. func (p *parsedFile) getFuncAST(f string, l int) (d *ast.FuncDecl) {
  107. if len(p.lineToByteOffset) <= l {
  108. // The line number in the stack trace line does not exist in the file. That
  109. // can only mean that the sources on disk do not match the sources used to
  110. // build the binary.
  111. // TODO(maruel): This should be surfaced, so that source parsing is
  112. // completely ignored.
  113. return
  114. }
  115. // Walk the AST to find the lineToByteOffset that fits the line number.
  116. var lastFunc *ast.FuncDecl
  117. var found ast.Node
  118. // Inspect() goes depth first. This means for example that a function like:
  119. // func a() {
  120. // b := func() {}
  121. // c()
  122. // }
  123. //
  124. // Were we are looking at the c() call can return confused values. It is
  125. // important to look at the actual ast.Node hierarchy.
  126. ast.Inspect(p.parsed, func(n ast.Node) bool {
  127. if d != nil {
  128. return false
  129. }
  130. if n == nil {
  131. return true
  132. }
  133. if found != nil {
  134. // We are walking up.
  135. }
  136. if int(n.Pos()) >= p.lineToByteOffset[l] {
  137. // We are expecting a ast.CallExpr node. It can be harder to figure out
  138. // when there are multiple calls on a single line, as the stack trace
  139. // doesn't have file byte offset information, only line based.
  140. // gofmt will always format to one function call per line but there can
  141. // be edge cases, like:
  142. // a = A{Foo(), Bar()}
  143. d = lastFunc
  144. //p.processNode(call, n)
  145. return false
  146. } else if f, ok := n.(*ast.FuncDecl); ok {
  147. lastFunc = f
  148. }
  149. return true
  150. })
  151. return
  152. }
  153. func name(n ast.Node) string {
  154. switch t := n.(type) {
  155. case *ast.InterfaceType:
  156. return "interface{}"
  157. case *ast.Ident:
  158. return t.Name
  159. case *ast.SelectorExpr:
  160. return t.Sel.Name
  161. case *ast.StarExpr:
  162. return "*" + name(t.X)
  163. default:
  164. return "<unknown>"
  165. }
  166. }
  167. // fieldToType returns the type name and whether if it's an ellipsis.
  168. func fieldToType(f *ast.Field) (string, bool) {
  169. switch arg := f.Type.(type) {
  170. case *ast.ArrayType:
  171. return "[]" + name(arg.Elt), false
  172. case *ast.Ellipsis:
  173. return name(arg.Elt), true
  174. case *ast.FuncType:
  175. // Do not print the function signature to not overload the trace.
  176. return "func", false
  177. case *ast.Ident:
  178. return arg.Name, false
  179. case *ast.InterfaceType:
  180. return "interface{}", false
  181. case *ast.SelectorExpr:
  182. return arg.Sel.Name, false
  183. case *ast.StarExpr:
  184. return "*" + name(arg.X), false
  185. case *ast.MapType:
  186. return fmt.Sprintf("map[%s]%s", name(arg.Key), name(arg.Value)), false
  187. case *ast.ChanType:
  188. return fmt.Sprintf("chan %s", name(arg.Value)), false
  189. default:
  190. // TODO(maruel): Implement anything missing.
  191. return "<unknown>", false
  192. }
  193. }
  194. // extractArgumentsType returns the name of the type of each input argument.
  195. func extractArgumentsType(f *ast.FuncDecl) ([]string, bool) {
  196. var fields []*ast.Field
  197. if f.Recv != nil {
  198. if len(f.Recv.List) != 1 {
  199. panic("Expect only one receiver; please fix panicparse's code")
  200. }
  201. // If it is an object receiver (vs a pointer receiver), its address is not
  202. // printed in the stack trace so it needs to be ignored.
  203. if _, ok := f.Recv.List[0].Type.(*ast.StarExpr); ok {
  204. fields = append(fields, f.Recv.List[0])
  205. }
  206. }
  207. var types []string
  208. extra := false
  209. for _, arg := range append(fields, f.Type.Params.List...) {
  210. // Assert that extra is only set on the last item of fields?
  211. var t string
  212. t, extra = fieldToType(arg)
  213. mult := len(arg.Names)
  214. if mult == 0 {
  215. mult = 1
  216. }
  217. for i := 0; i < mult; i++ {
  218. types = append(types, t)
  219. }
  220. }
  221. return types, extra
  222. }
  223. // processCall walks the function and populate call accordingly.
  224. func processCall(call *Call, f *ast.FuncDecl) {
  225. values := make([]uint64, len(call.Args.Values))
  226. for i := range call.Args.Values {
  227. values[i] = call.Args.Values[i].Value
  228. }
  229. index := 0
  230. pop := func() uint64 {
  231. if len(values) != 0 {
  232. x := values[0]
  233. values = values[1:]
  234. index++
  235. return x
  236. }
  237. return 0
  238. }
  239. popName := func() string {
  240. n := call.Args.Values[index].Name
  241. v := pop()
  242. if len(n) == 0 {
  243. return fmt.Sprintf("0x%x", v)
  244. }
  245. return n
  246. }
  247. types, extra := extractArgumentsType(f)
  248. for i := 0; len(values) != 0; i++ {
  249. var t string
  250. if i >= len(types) {
  251. if !extra {
  252. // These are unexpected value! Print them as hex.
  253. call.Args.Processed = append(call.Args.Processed, popName())
  254. continue
  255. }
  256. t = types[len(types)-1]
  257. } else {
  258. t = types[i]
  259. }
  260. switch t {
  261. case "float32":
  262. call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%g", math.Float32frombits(uint32(pop()))))
  263. case "float64":
  264. call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%g", math.Float64frombits(pop())))
  265. case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
  266. call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%d", pop()))
  267. case "string":
  268. call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s, len=%d)", t, popName(), pop()))
  269. default:
  270. if strings.HasPrefix(t, "*") {
  271. call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s)", t, popName()))
  272. } else if strings.HasPrefix(t, "[]") {
  273. call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s len=%d cap=%d)", t, popName(), pop(), pop()))
  274. } else {
  275. // Assumes it's an interface. For now, discard the object value, which
  276. // is probably not a good idea.
  277. call.Args.Processed = append(call.Args.Processed, fmt.Sprintf("%s(%s)", t, popName()))
  278. pop()
  279. }
  280. }
  281. if len(values) == 0 && call.Args.Elided {
  282. return
  283. }
  284. }
  285. }