source.go 7.8 KB

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