source_test.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  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. package stack
  5. import (
  6. "bytes"
  7. "fmt"
  8. "io/ioutil"
  9. "os"
  10. "os/exec"
  11. "path/filepath"
  12. "reflect"
  13. "strings"
  14. "testing"
  15. )
  16. func TestAugment(t *testing.T) {
  17. data := []struct {
  18. name string
  19. input string
  20. expected Stack
  21. }{
  22. {
  23. "Local function doesn't interfere",
  24. `package main
  25. func f(s string) {
  26. a := func(i int) int {
  27. return 1 + i
  28. }
  29. _ = a(3)
  30. panic("ooh")
  31. }
  32. func main() {
  33. f("yo")
  34. }`,
  35. Stack{
  36. Calls: []Call{
  37. {
  38. SrcPath: "main.go", Line: 7, Func: Func{Raw: "main.f"},
  39. Args: Args{
  40. Values: []Arg{{Value: pointer, Name: ""}, {Value: 0x2}},
  41. },
  42. },
  43. {SrcPath: "main.go", Line: 10, Func: Func{Raw: "main.main"}},
  44. },
  45. },
  46. },
  47. {
  48. "func",
  49. `package main
  50. func f(a func() string) {
  51. panic(a())
  52. }
  53. func main() {
  54. f(func() string { return "ooh" })
  55. }`,
  56. Stack{
  57. Calls: []Call{
  58. {
  59. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  60. Args: Args{Values: []Arg{{Value: pointer}}},
  61. },
  62. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  63. },
  64. },
  65. },
  66. {
  67. "func ellipsis",
  68. `package main
  69. func f(a ...func() string) {
  70. panic(a[0]())
  71. }
  72. func main() {
  73. f(func() string { return "ooh" })
  74. }`,
  75. Stack{
  76. Calls: []Call{
  77. {
  78. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  79. Args: Args{
  80. Values: []Arg{{Value: pointer}, {Value: 0x1}, {Value: 0x1}},
  81. },
  82. },
  83. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  84. },
  85. },
  86. },
  87. {
  88. "interface{}",
  89. `package main
  90. func f(a []interface{}) {
  91. panic("ooh")
  92. }
  93. func main() {
  94. f(make([]interface{}, 5, 7))
  95. }`,
  96. Stack{
  97. Calls: []Call{
  98. {
  99. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  100. Args: Args{
  101. Values: []Arg{{Value: pointer}, {Value: 0x5}, {Value: 0x7}},
  102. },
  103. },
  104. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  105. },
  106. },
  107. },
  108. {
  109. "[]int",
  110. `package main
  111. func f(a []int) {
  112. panic("ooh")
  113. }
  114. func main() {
  115. f(make([]int, 5, 7))
  116. }`,
  117. Stack{
  118. Calls: []Call{
  119. {
  120. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  121. Args: Args{
  122. Values: []Arg{{Value: pointer}, {Value: 5}, {Value: 7}},
  123. },
  124. },
  125. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  126. },
  127. },
  128. },
  129. {
  130. "[]interface{}",
  131. `package main
  132. func f(a []interface{}) {
  133. panic(a[0].(string))
  134. }
  135. func main() {
  136. f([]interface{}{"ooh"})
  137. }`,
  138. Stack{
  139. Calls: []Call{
  140. {
  141. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  142. Args: Args{
  143. Values: []Arg{{Value: pointer}, {Value: 1}, {Value: 1}},
  144. },
  145. },
  146. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  147. },
  148. },
  149. },
  150. {
  151. "map[int]int",
  152. `package main
  153. func f(a map[int]int) {
  154. panic("ooh")
  155. }
  156. func main() {
  157. f(map[int]int{1: 2})
  158. }`,
  159. Stack{
  160. Calls: []Call{
  161. {
  162. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  163. Args: Args{
  164. Values: []Arg{{Value: pointer}},
  165. },
  166. },
  167. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  168. },
  169. },
  170. },
  171. {
  172. "map[interface{}]interface{}",
  173. `package main
  174. func f(a map[interface{}]interface{}) {
  175. panic("ooh")
  176. }
  177. func main() {
  178. f(make(map[interface{}]interface{}))
  179. }`,
  180. Stack{
  181. Calls: []Call{
  182. {
  183. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  184. Args: Args{
  185. Values: []Arg{{Value: pointer}},
  186. },
  187. },
  188. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  189. },
  190. },
  191. },
  192. {
  193. "chan int",
  194. `package main
  195. func f(a chan int) {
  196. panic("ooh")
  197. }
  198. func main() {
  199. f(make(chan int))
  200. }`,
  201. Stack{
  202. Calls: []Call{
  203. {
  204. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  205. Args: Args{
  206. Values: []Arg{{Value: pointer}},
  207. },
  208. },
  209. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  210. },
  211. },
  212. },
  213. {
  214. "chan interface{}",
  215. `package main
  216. func f(a chan interface{}) {
  217. panic("ooh")
  218. }
  219. func main() {
  220. f(make(chan interface{}))
  221. }`,
  222. Stack{
  223. Calls: []Call{
  224. {
  225. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  226. Args: Args{
  227. Values: []Arg{{Value: pointer}},
  228. },
  229. },
  230. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  231. },
  232. },
  233. },
  234. {
  235. "non-pointer method",
  236. `package main
  237. type S struct {
  238. }
  239. func (s S) f() {
  240. panic("ooh")
  241. }
  242. func main() {
  243. var s S
  244. s.f()
  245. }`,
  246. Stack{
  247. Calls: []Call{
  248. {SrcPath: "main.go", Line: 5, Func: Func{Raw: "main.S.f"}},
  249. {SrcPath: "main.go", Line: 9, Func: Func{Raw: "main.main"}},
  250. },
  251. },
  252. },
  253. {
  254. "pointer method",
  255. `package main
  256. type S struct {
  257. }
  258. func (s *S) f() {
  259. panic("ooh")
  260. }
  261. func main() {
  262. var s S
  263. s.f()
  264. }`,
  265. Stack{
  266. Calls: []Call{
  267. {
  268. SrcPath: "main.go", Line: 5, Func: Func{Raw: "main.(*S).f"},
  269. Args: Args{Values: []Arg{{Value: pointer}}},
  270. },
  271. {SrcPath: "main.go", Line: 9, Func: Func{Raw: "main.main"}},
  272. },
  273. },
  274. },
  275. {
  276. "string",
  277. `package main
  278. func f(s string) {
  279. panic(s)
  280. }
  281. func main() {
  282. f("ooh")
  283. }`,
  284. Stack{
  285. Calls: []Call{
  286. {
  287. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  288. Args: Args{Values: []Arg{{Value: pointer}, {Value: 0x3}}},
  289. },
  290. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  291. },
  292. },
  293. },
  294. {
  295. "string and int",
  296. `package main
  297. func f(s string, i int) {
  298. panic(s)
  299. }
  300. func main() {
  301. f("ooh", 42)
  302. }`,
  303. Stack{
  304. Calls: []Call{
  305. {
  306. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  307. Args: Args{Values: []Arg{{Value: pointer}, {Value: 0x3}, {Value: 42}}},
  308. },
  309. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  310. },
  311. },
  312. },
  313. {
  314. "values are elided",
  315. `package main
  316. func f(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12 int, s13 interface{}) {
  317. panic("ooh")
  318. }
  319. func main() {
  320. f(0, 0, 0, 0, 0, 0, 0, 0, 42, 43, 44, 45, nil)
  321. }`,
  322. Stack{
  323. Calls: []Call{
  324. {
  325. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  326. Args: Args{
  327. Values: []Arg{{}, {}, {}, {}, {}, {}, {}, {}, {Value: 42}, {Value: 43}},
  328. Elided: true,
  329. },
  330. },
  331. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  332. },
  333. },
  334. },
  335. {
  336. "error",
  337. `package main
  338. import "errors"
  339. func f(err error) {
  340. panic(err.Error())
  341. }
  342. func main() {
  343. f(errors.New("ooh"))
  344. }`,
  345. Stack{
  346. Calls: []Call{
  347. {
  348. SrcPath: "main.go", Line: 4, Func: Func{Raw: "main.f"},
  349. Args: Args{
  350. Values: []Arg{{Value: pointer}, {Value: pointer}},
  351. },
  352. },
  353. {SrcPath: "main.go", Line: 7, Func: Func{Raw: "main.main"}},
  354. },
  355. },
  356. },
  357. {
  358. "error unnamed",
  359. `package main
  360. import "errors"
  361. func f(error) {
  362. panic("ooh")
  363. }
  364. func main() {
  365. f(errors.New("ooh"))
  366. }`,
  367. Stack{
  368. Calls: []Call{
  369. {
  370. SrcPath: "main.go", Line: 4, Func: Func{Raw: "main.f"},
  371. Args: Args{
  372. Values: []Arg{{Value: pointer}, {Value: pointer}},
  373. },
  374. },
  375. {SrcPath: "main.go", Line: 7, Func: Func{Raw: "main.main"}},
  376. },
  377. },
  378. },
  379. {
  380. "float32",
  381. `package main
  382. func f(v float32) {
  383. panic("ooh")
  384. }
  385. func main() {
  386. f(0.5)
  387. }`,
  388. Stack{
  389. Calls: []Call{
  390. {
  391. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  392. Args: Args{
  393. // The value is NOT a pointer but floating point encoding is not
  394. // deterministic.
  395. Values: []Arg{{Value: pointer}},
  396. },
  397. },
  398. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  399. },
  400. },
  401. },
  402. {
  403. "float64",
  404. `package main
  405. func f(v float64) {
  406. panic("ooh")
  407. }
  408. func main() {
  409. f(0.5)
  410. }`,
  411. Stack{
  412. Calls: []Call{
  413. {
  414. SrcPath: "main.go", Line: 3, Func: Func{Raw: "main.f"},
  415. Args: Args{
  416. // The value is NOT a pointer but floating point encoding is not
  417. // deterministic.
  418. Values: []Arg{{Value: pointer}},
  419. },
  420. },
  421. {SrcPath: "main.go", Line: 6, Func: Func{Raw: "main.main"}},
  422. },
  423. },
  424. },
  425. }
  426. for i, line := range data {
  427. extra := bytes.Buffer{}
  428. _, content, clean := getCrash(t, line.input)
  429. c, err := ParseDump(bytes.NewBuffer(content), &extra, false)
  430. if err != nil {
  431. clean()
  432. t.Fatalf("failed to parse input for test %s: %v", line.name, err)
  433. }
  434. // On go1.4, there's one less space.
  435. actual := extra.String()
  436. if actual != "panic: ooh\n\nexit status 2\n" && actual != "panic: ooh\nexit status 2\n" {
  437. clean()
  438. t.Fatalf("Unexpected panic output:\n%#v", actual)
  439. }
  440. s := c.Goroutines[0].Signature.Stack
  441. t.Logf("Test: %v", line.name)
  442. zapPointers(t, line.name, &line.expected, &s)
  443. zapPaths(&s)
  444. clean()
  445. if !reflect.DeepEqual(line.expected, s) {
  446. t.Fatalf("#%d: Different:\n- %v\n- %v", i, line.expected, s)
  447. }
  448. }
  449. }
  450. func TestAugmentDummy(t *testing.T) {
  451. goroutines := []*Goroutine{
  452. {
  453. Signature: Signature{
  454. Stack: Stack{
  455. Calls: []Call{{SrcPath: "missing.go"}},
  456. },
  457. },
  458. },
  459. }
  460. Augment(goroutines)
  461. }
  462. func TestLoad(t *testing.T) {
  463. c := &cache{
  464. files: map[string][]byte{"bad.go": []byte("bad content")},
  465. parsed: map[string]*parsedFile{},
  466. }
  467. c.load("foo.asm")
  468. c.load("bad.go")
  469. c.load("doesnt_exist.go")
  470. if l := len(c.parsed); l != 3 {
  471. t.Fatalf("expected 3, got %d", l)
  472. }
  473. if c.parsed["foo.asm"] != nil {
  474. t.Fatalf("foo.asm is not present; should not have been loaded")
  475. }
  476. if c.parsed["bad.go"] != nil {
  477. t.Fatalf("bad.go is not valid code; should not have been loaded")
  478. }
  479. if c.parsed["doesnt_exist.go"] != nil {
  480. t.Fatalf("doesnt_exist.go is not present; should not have been loaded")
  481. }
  482. if c.getFuncAST(&Call{SrcPath: "other"}) != nil {
  483. t.Fatalf("there's no 'other'")
  484. }
  485. }
  486. //
  487. const pointer = uint64(0xfffffffff)
  488. const pointerStr = "0xfffffffff"
  489. func overrideEnv(env []string, key, value string) []string {
  490. prefix := key + "="
  491. for i, e := range env {
  492. if strings.HasPrefix(e, prefix) {
  493. env[i] = prefix + value
  494. return env
  495. }
  496. }
  497. return append(env, prefix+value)
  498. }
  499. func getCrash(t *testing.T, content string) (string, []byte, func()) {
  500. //p := getGOPATHs()
  501. //name, err := ioutil.TempDir(filepath.Join(p[0], "src"), "panicparse")
  502. name, err := ioutil.TempDir("", "panicparse")
  503. if err != nil {
  504. t.Fatalf("failed to create temporary directory: %v", err)
  505. }
  506. clean := func() {
  507. if err := os.RemoveAll(name); err != nil {
  508. t.Fatalf("failed to remove temporary directory %q: %v", name, err)
  509. }
  510. }
  511. main := filepath.Join(name, "main.go")
  512. if err := ioutil.WriteFile(main, []byte(content), 0500); err != nil {
  513. clean()
  514. t.Fatalf("failed to write %q: %v", main, err)
  515. }
  516. cmd := exec.Command("go", "run", main)
  517. // Use the Go 1.4 compatible format.
  518. cmd.Env = overrideEnv(os.Environ(), "GOTRACEBACK", "1")
  519. out, err := cmd.CombinedOutput()
  520. if err == nil {
  521. clean()
  522. t.Fatal("expected error since this is supposed to crash")
  523. }
  524. return main, out, clean
  525. }
  526. // zapPointers zaps out pointers.
  527. func zapPointers(t *testing.T, name string, expected, s *Stack) {
  528. for i := range s.Calls {
  529. if i >= len(expected.Calls) {
  530. // When using GOTRACEBACK=2, it'll include runtime.main() and
  531. // runtime.goexit(). Ignore these since they could be changed in a future
  532. // version.
  533. s.Calls = s.Calls[:len(expected.Calls)]
  534. break
  535. }
  536. for j := range s.Calls[i].Args.Values {
  537. if j >= len(expected.Calls[i].Args.Values) {
  538. break
  539. }
  540. if expected.Calls[i].Args.Values[j].Value == pointer {
  541. // Replace the pointer value.
  542. if s.Calls[i].Args.Values[j].Value == 0 {
  543. t.Fatalf("%s: Call %d, value %d, expected pointer, got 0", name, i, j)
  544. }
  545. old := fmt.Sprintf("0x%x", s.Calls[i].Args.Values[j].Value)
  546. s.Calls[i].Args.Values[j].Value = pointer
  547. for k := range s.Calls[i].Args.Processed {
  548. s.Calls[i].Args.Processed[k] = strings.Replace(s.Calls[i].Args.Processed[k], old, pointerStr, -1)
  549. }
  550. }
  551. }
  552. }
  553. }
  554. // zapPaths removes the directory part and only keep the base file name.
  555. func zapPaths(s *Stack) {
  556. for j := range s.Calls {
  557. s.Calls[j].SrcPath = filepath.Base(s.Calls[j].SrcPath)
  558. s.Calls[j].LocalSrcPath = ""
  559. }
  560. }