context_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678
  1. // Copyright 2018 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. "bufio"
  7. "bytes"
  8. "errors"
  9. "io/ioutil"
  10. "reflect"
  11. "strings"
  12. "testing"
  13. )
  14. func TestParseDump1(t *testing.T) {
  15. // One call from main, one from stdlib, one from third party.
  16. // Create a long first line that will be ignored. It is to guard against
  17. // https://notabug.org/themusicgod1/panicparse/issues/17.
  18. long := strings.Repeat("a", bufio.MaxScanTokenSize+1)
  19. data := []string{
  20. long,
  21. "panic: reflect.Set: value of type",
  22. "",
  23. "goroutine 1 [running]:",
  24. "github.com/cockroachdb/cockroach/storage/engine._Cfunc_DBIterSeek()",
  25. " ??:0 +0x6d",
  26. "gopkg.in/yaml%2ev2.handleErr(0xc208033b20)",
  27. " /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
  28. "reflect.Value.assignTo(0x570860, 0xc20803f3e0, 0x15)",
  29. " /goroot/src/reflect/value.go:2125 +0x368",
  30. "main.main()",
  31. " /gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:428 +0x27",
  32. "",
  33. }
  34. extra := &bytes.Buffer{}
  35. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, true)
  36. if err != nil {
  37. t.Fatal(err)
  38. }
  39. compareString(t, long+"\npanic: reflect.Set: value of type\n\n", extra.String())
  40. expected := []*Goroutine{
  41. {
  42. Signature: Signature{
  43. State: "running",
  44. Stack: Stack{
  45. Calls: []Call{
  46. {
  47. SrcPath: "??",
  48. Func: Func{Raw: "github.com/cockroachdb/cockroach/storage/engine._Cfunc_DBIterSeek"},
  49. },
  50. {
  51. SrcPath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
  52. Line: 153,
  53. Func: Func{Raw: "gopkg.in/yaml%2ev2.handleErr"},
  54. Args: Args{Values: []Arg{{Value: 0xc208033b20}}},
  55. },
  56. {
  57. SrcPath: "/goroot/src/reflect/value.go",
  58. Line: 2125,
  59. Func: Func{Raw: "reflect.Value.assignTo"},
  60. Args: Args{Values: []Arg{{Value: 0x570860}, {Value: 0xc20803f3e0}, {Value: 0x15}}},
  61. },
  62. {
  63. SrcPath: "/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go",
  64. Line: 428,
  65. Func: Func{Raw: "main.main"},
  66. },
  67. },
  68. },
  69. },
  70. ID: 1,
  71. First: true,
  72. },
  73. }
  74. for i := range expected {
  75. expected[i].updateLocations(c.GOROOT, c.localgoroot, c.GOPATHs)
  76. }
  77. compareGoroutines(t, expected, c.Goroutines)
  78. }
  79. func TestParseDumpLongWait(t *testing.T) {
  80. // One call from main, one from stdlib, one from third party.
  81. data := []string{
  82. "panic: bleh",
  83. "",
  84. "goroutine 1 [chan send, 100 minutes]:",
  85. "gopkg.in/yaml%2ev2.handleErr(0xc208033b20)",
  86. " /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
  87. "",
  88. "goroutine 2 [chan send, locked to thread]:",
  89. "gopkg.in/yaml%2ev2.handleErr(0xc208033b21)",
  90. " /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
  91. "",
  92. "goroutine 3 [chan send, 101 minutes, locked to thread]:",
  93. "gopkg.in/yaml%2ev2.handleErr(0xc208033b22)",
  94. " /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
  95. "",
  96. }
  97. extra := &bytes.Buffer{}
  98. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, true)
  99. if err != nil {
  100. t.Fatal(err)
  101. }
  102. compareString(t, "panic: bleh\n\n", extra.String())
  103. expected := []*Goroutine{
  104. {
  105. Signature: Signature{
  106. State: "chan send",
  107. SleepMin: 100,
  108. SleepMax: 100,
  109. Stack: Stack{
  110. Calls: []Call{
  111. {
  112. SrcPath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
  113. Line: 153,
  114. Func: Func{Raw: "gopkg.in/yaml%2ev2.handleErr"},
  115. Args: Args{Values: []Arg{{Value: 0xc208033b20}}},
  116. },
  117. },
  118. },
  119. },
  120. ID: 1,
  121. First: true,
  122. },
  123. {
  124. Signature: Signature{
  125. State: "chan send",
  126. Locked: true,
  127. Stack: Stack{
  128. Calls: []Call{
  129. {
  130. SrcPath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
  131. Line: 153,
  132. Func: Func{Raw: "gopkg.in/yaml%2ev2.handleErr"},
  133. Args: Args{Values: []Arg{{Value: 0xc208033b21, Name: "#1"}}},
  134. },
  135. },
  136. },
  137. },
  138. ID: 2,
  139. },
  140. {
  141. Signature: Signature{
  142. State: "chan send",
  143. SleepMin: 101,
  144. SleepMax: 101,
  145. Stack: Stack{
  146. Calls: []Call{
  147. {
  148. SrcPath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
  149. Line: 153,
  150. Func: Func{Raw: "gopkg.in/yaml%2ev2.handleErr"},
  151. Args: Args{Values: []Arg{{Value: 0xc208033b22, Name: "#2"}}},
  152. },
  153. },
  154. },
  155. Locked: true,
  156. },
  157. ID: 3,
  158. },
  159. }
  160. for i := range expected {
  161. expected[i].updateLocations(c.GOROOT, c.localgoroot, c.GOPATHs)
  162. }
  163. compareGoroutines(t, expected, c.Goroutines)
  164. }
  165. func TestParseDumpAsm(t *testing.T) {
  166. data := []string{
  167. "panic: reflect.Set: value of type",
  168. "",
  169. "goroutine 16 [garbage collection]:",
  170. "runtime.switchtoM()",
  171. "\t/goroot/src/runtime/asm_amd64.s:198 fp=0xc20cfb80d8 sp=0xc20cfb80d0",
  172. "",
  173. }
  174. extra := &bytes.Buffer{}
  175. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, false)
  176. if err != nil {
  177. t.Fatal(err)
  178. }
  179. expected := []*Goroutine{
  180. {
  181. Signature: Signature{
  182. State: "garbage collection",
  183. Stack: Stack{
  184. Calls: []Call{
  185. {
  186. SrcPath: "/goroot/src/runtime/asm_amd64.s",
  187. Line: 198,
  188. Func: Func{Raw: "runtime.switchtoM"},
  189. },
  190. },
  191. },
  192. },
  193. ID: 16,
  194. First: true,
  195. },
  196. }
  197. compareGoroutines(t, expected, c.Goroutines)
  198. compareString(t, "panic: reflect.Set: value of type\n\n", extra.String())
  199. }
  200. func TestParseDumpLineErr(t *testing.T) {
  201. data := []string{
  202. "panic: reflect.Set: value of type",
  203. "",
  204. "goroutine 1 [running]:",
  205. "notabug.org/themusicgod1/panicparse/stack/stack.recurseType()",
  206. "\t/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:12345678901234567890",
  207. "",
  208. }
  209. extra := &bytes.Buffer{}
  210. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, false)
  211. compareErr(t, errors.New("failed to parse int on line: \"/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:12345678901234567890\""), err)
  212. expected := []*Goroutine{
  213. {
  214. Signature: Signature{
  215. State: "running",
  216. Stack: Stack{Calls: []Call{{Func: Func{Raw: "notabug.org/themusicgod1/panicparse/stack/stack.recurseType"}}}},
  217. },
  218. ID: 1,
  219. First: true,
  220. },
  221. }
  222. for i := range expected {
  223. expected[i].updateLocations(c.GOROOT, c.localgoroot, c.GOPATHs)
  224. }
  225. compareGoroutines(t, expected, c.Goroutines)
  226. }
  227. func TestParseDumpValueErr(t *testing.T) {
  228. data := []string{
  229. "panic: reflect.Set: value of type",
  230. "",
  231. "goroutine 1 [running]:",
  232. "notabug.org/themusicgod1/panicparse/stack/stack.recurseType(123456789012345678901)",
  233. "\t/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:9",
  234. "",
  235. }
  236. extra := &bytes.Buffer{}
  237. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, false)
  238. compareErr(t, errors.New("failed to parse int on line: \"notabug.org/themusicgod1/panicparse/stack/stack.recurseType(123456789012345678901)\""), err)
  239. expected := []*Goroutine{
  240. {
  241. Signature: Signature{State: "running"},
  242. ID: 1,
  243. First: true,
  244. },
  245. }
  246. for i := range expected {
  247. expected[i].updateLocations(c.GOROOT, c.localgoroot, c.GOPATHs)
  248. }
  249. compareGoroutines(t, expected, c.Goroutines)
  250. }
  251. func TestParseDumpOrderErr(t *testing.T) {
  252. data := []string{
  253. "panic: reflect.Set: value of type",
  254. "",
  255. "goroutine 16 [garbage collection]:",
  256. " /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
  257. "runtime.switchtoM()",
  258. "\t/goroot/src/runtime/asm_amd64.s:198 fp=0xc20cfb80d8 sp=0xc20cfb80d0",
  259. "",
  260. }
  261. extra := &bytes.Buffer{}
  262. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, false)
  263. compareErr(t, errors.New("unexpected order on line: \"/gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6\""), err)
  264. expected := []*Goroutine{
  265. {
  266. Signature: Signature{State: "garbage collection"},
  267. ID: 16,
  268. First: true,
  269. },
  270. }
  271. compareGoroutines(t, expected, c.Goroutines)
  272. compareString(t, "panic: reflect.Set: value of type\n\n", extra.String())
  273. }
  274. func TestParseDumpElided(t *testing.T) {
  275. data := []string{
  276. "panic: reflect.Set: value of type",
  277. "",
  278. "goroutine 16 [garbage collection]:",
  279. "notabug.org/themusicgod1/panicparse/stack/stack.recurseType(0x7f4fa9a3ec70, 0xc208062580, 0x7f4fa9a3e818, 0x50a820, 0xc20803a8a0)",
  280. "\t/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:53 +0x845 fp=0xc20cfc66d8 sp=0xc20cfc6470",
  281. "...additional frames elided...",
  282. "created by testing.RunTests",
  283. "\t/goroot/src/testing/testing.go:555 +0xa8b",
  284. "",
  285. }
  286. extra := &bytes.Buffer{}
  287. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, false)
  288. if err != nil {
  289. t.Fatal(err)
  290. }
  291. expected := []*Goroutine{
  292. {
  293. Signature: Signature{
  294. State: "garbage collection",
  295. Stack: Stack{
  296. Calls: []Call{
  297. {
  298. SrcPath: "/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go",
  299. Line: 53,
  300. Func: Func{Raw: "notabug.org/themusicgod1/panicparse/stack/stack.recurseType"},
  301. Args: Args{
  302. Values: []Arg{
  303. {Value: 0x7f4fa9a3ec70},
  304. {Value: 0xc208062580},
  305. {Value: 0x7f4fa9a3e818},
  306. {Value: 0x50a820},
  307. {Value: 0xc20803a8a0},
  308. },
  309. },
  310. },
  311. },
  312. Elided: true,
  313. },
  314. CreatedBy: Call{
  315. SrcPath: "/goroot/src/testing/testing.go",
  316. Line: 555,
  317. Func: Func{Raw: "testing.RunTests"},
  318. },
  319. },
  320. ID: 16,
  321. First: true,
  322. },
  323. }
  324. compareGoroutines(t, expected, c.Goroutines)
  325. compareString(t, "panic: reflect.Set: value of type\n\n", extra.String())
  326. }
  327. func TestParseDumpSysCall(t *testing.T) {
  328. data := []string{
  329. "panic: reflect.Set: value of type",
  330. "",
  331. "goroutine 5 [syscall]:",
  332. "runtime.notetsleepg(0x918100, 0xffffffffffffffff, 0x1)",
  333. "\t/goroot/src/runtime/lock_futex.go:201 +0x52 fp=0xc208018f68 sp=0xc208018f40",
  334. "runtime.signal_recv(0x0)",
  335. "\t/goroot/src/runtime/sigqueue.go:109 +0x135 fp=0xc208018fa0 sp=0xc208018f68",
  336. "os/signal.loop()",
  337. "\t/goroot/src/os/signal/signal_unix.go:21 +0x1f fp=0xc208018fe0 sp=0xc208018fa0",
  338. "runtime.goexit()",
  339. "\t/goroot/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc208018fe8 sp=0xc208018fe0",
  340. "created by os/signal.init·1",
  341. "\t/goroot/src/os/signal/signal_unix.go:27 +0x35",
  342. "",
  343. }
  344. extra := &bytes.Buffer{}
  345. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, false)
  346. if err != nil {
  347. t.Fatal(err)
  348. }
  349. expected := []*Goroutine{
  350. {
  351. Signature: Signature{
  352. State: "syscall",
  353. Stack: Stack{
  354. Calls: []Call{
  355. {
  356. SrcPath: "/goroot/src/runtime/lock_futex.go",
  357. Line: 201,
  358. Func: Func{Raw: "runtime.notetsleepg"},
  359. Args: Args{
  360. Values: []Arg{
  361. {Value: 0x918100},
  362. {Value: 0xffffffffffffffff},
  363. {Value: 0x1},
  364. },
  365. },
  366. },
  367. {
  368. SrcPath: "/goroot/src/runtime/sigqueue.go",
  369. Line: 109,
  370. Func: Func{Raw: "runtime.signal_recv"},
  371. Args: Args{
  372. Values: []Arg{{}},
  373. },
  374. },
  375. {
  376. SrcPath: "/goroot/src/os/signal/signal_unix.go",
  377. Line: 21,
  378. Func: Func{Raw: "os/signal.loop"},
  379. },
  380. {
  381. SrcPath: "/goroot/src/runtime/asm_amd64.s",
  382. Line: 2232,
  383. Func: Func{Raw: "runtime.goexit"},
  384. },
  385. },
  386. },
  387. CreatedBy: Call{
  388. SrcPath: "/goroot/src/os/signal/signal_unix.go",
  389. Line: 27,
  390. Func: Func{Raw: "os/signal.init·1"},
  391. },
  392. },
  393. ID: 5,
  394. First: true,
  395. },
  396. }
  397. compareGoroutines(t, expected, c.Goroutines)
  398. compareString(t, "panic: reflect.Set: value of type\n\n", extra.String())
  399. }
  400. func TestParseDumpUnavail(t *testing.T) {
  401. data := []string{
  402. "panic: reflect.Set: value of type",
  403. "",
  404. "goroutine 24 [running]:",
  405. "\tgoroutine running on other thread; stack unavailable",
  406. "created by notabug.org/themusicgod1/panicparse/stack.New",
  407. "\t/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:131 +0x381",
  408. "",
  409. }
  410. extra := &bytes.Buffer{}
  411. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), extra, false)
  412. if err != nil {
  413. t.Fatal(err)
  414. }
  415. expected := []*Goroutine{
  416. {
  417. Signature: Signature{
  418. State: "running",
  419. Stack: Stack{
  420. Calls: []Call{{SrcPath: "<unavailable>"}},
  421. },
  422. CreatedBy: Call{
  423. SrcPath: "/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go",
  424. Line: 131,
  425. Func: Func{Raw: "notabug.org/themusicgod1/panicparse/stack.New"},
  426. },
  427. },
  428. ID: 24,
  429. First: true,
  430. },
  431. }
  432. compareGoroutines(t, expected, c.Goroutines)
  433. compareString(t, "panic: reflect.Set: value of type\n\n", extra.String())
  434. }
  435. func TestParseDumpNoOffset(t *testing.T) {
  436. data := []string{
  437. "panic: runtime error: index out of range",
  438. "",
  439. "goroutine 37 [runnable]:",
  440. "notabug.org/themusicgod1/panicparse/stack.func·002()",
  441. " /gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:110",
  442. "created by notabug.org/themusicgod1/panicparse/stack.New",
  443. " /gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:113 +0x43b",
  444. "",
  445. }
  446. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), ioutil.Discard, false)
  447. if err != nil {
  448. t.Fatal(err)
  449. }
  450. expectedGR := []*Goroutine{
  451. {
  452. Signature: Signature{
  453. State: "runnable",
  454. Stack: Stack{
  455. Calls: []Call{
  456. {
  457. SrcPath: "/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go",
  458. Line: 110,
  459. Func: Func{Raw: "notabug.org/themusicgod1/panicparse/stack.func·002"},
  460. },
  461. },
  462. },
  463. CreatedBy: Call{
  464. SrcPath: "/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go",
  465. Line: 113,
  466. Func: Func{Raw: "notabug.org/themusicgod1/panicparse/stack.New"},
  467. },
  468. },
  469. ID: 37,
  470. First: true,
  471. },
  472. }
  473. compareGoroutines(t, expectedGR, c.Goroutines)
  474. }
  475. func TestParseDumpJunk(t *testing.T) {
  476. // For coverage of scanLines.
  477. data := []string{
  478. "panic: reflect.Set: value of type",
  479. "",
  480. "goroutine 1 [running]:",
  481. "junk",
  482. }
  483. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), ioutil.Discard, false)
  484. if err != nil {
  485. t.Fatal(err)
  486. }
  487. expectedGR := []*Goroutine{
  488. {
  489. Signature: Signature{State: "running"},
  490. ID: 1,
  491. First: true,
  492. },
  493. }
  494. compareGoroutines(t, expectedGR, c.Goroutines)
  495. }
  496. func TestParseDumpCCode(t *testing.T) {
  497. data := []string{
  498. "SIGQUIT: quit",
  499. "PC=0x43f349",
  500. "",
  501. "goroutine 0 [idle]:",
  502. "runtime.epollwait(0x4, 0x7fff671c7118, 0xffffffff00000080, 0x0, 0xffffffff0028c1be, 0x0, 0x0, 0x0, 0x0, 0x0, ...)",
  503. " /goroot/src/runtime/sys_linux_amd64.s:400 +0x19",
  504. "runtime.netpoll(0x901b01, 0x0)",
  505. " /goroot/src/runtime/netpoll_epoll.go:68 +0xa3",
  506. "findrunnable(0xc208012000)",
  507. " /goroot/src/runtime/proc.c:1472 +0x485",
  508. "schedule()",
  509. " /goroot/src/runtime/proc.c:1575 +0x151",
  510. "runtime.park_m(0xc2080017a0)",
  511. " /goroot/src/runtime/proc.c:1654 +0x113",
  512. "runtime.mcall(0x432684)",
  513. " /goroot/src/runtime/asm_amd64.s:186 +0x5a",
  514. "",
  515. }
  516. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\n")), ioutil.Discard, false)
  517. if err != nil {
  518. t.Fatal(err)
  519. }
  520. expectedGR := []*Goroutine{
  521. {
  522. Signature: Signature{
  523. State: "idle",
  524. Stack: Stack{
  525. Calls: []Call{
  526. {
  527. SrcPath: "/goroot/src/runtime/sys_linux_amd64.s",
  528. Line: 400,
  529. Func: Func{Raw: "runtime.epollwait"},
  530. Args: Args{
  531. Values: []Arg{
  532. {Value: 0x4},
  533. {Value: 0x7fff671c7118},
  534. {Value: 0xffffffff00000080},
  535. {},
  536. {Value: 0xffffffff0028c1be},
  537. {},
  538. {},
  539. {},
  540. {},
  541. {},
  542. },
  543. Elided: true,
  544. },
  545. },
  546. {
  547. SrcPath: "/goroot/src/runtime/netpoll_epoll.go",
  548. Line: 68,
  549. Func: Func{Raw: "runtime.netpoll"},
  550. Args: Args{Values: []Arg{{Value: 0x901b01}, {}}},
  551. },
  552. {
  553. SrcPath: "/goroot/src/runtime/proc.c",
  554. Line: 1472,
  555. Func: Func{Raw: "findrunnable"},
  556. Args: Args{Values: []Arg{{Value: 0xc208012000}}},
  557. },
  558. {
  559. SrcPath: "/goroot/src/runtime/proc.c",
  560. Line: 1575,
  561. Func: Func{Raw: "schedule"},
  562. },
  563. {
  564. SrcPath: "/goroot/src/runtime/proc.c",
  565. Line: 1654,
  566. Func: Func{Raw: "runtime.park_m"},
  567. Args: Args{Values: []Arg{{Value: 0xc2080017a0}}},
  568. },
  569. {
  570. SrcPath: "/goroot/src/runtime/asm_amd64.s",
  571. Line: 186,
  572. Func: Func{Raw: "runtime.mcall"},
  573. Args: Args{Values: []Arg{{Value: 0x432684}}},
  574. },
  575. },
  576. },
  577. },
  578. ID: 0,
  579. First: true,
  580. },
  581. }
  582. compareGoroutines(t, expectedGR, c.Goroutines)
  583. }
  584. func TestParseDumpWithCarriageReturn(t *testing.T) {
  585. data := []string{
  586. "goroutine 1 [running]:",
  587. "github.com/cockroachdb/cockroach/storage/engine._Cfunc_DBIterSeek()",
  588. " ??:0 +0x6d",
  589. "gopkg.in/yaml%2ev2.handleErr(0xc208033b20)",
  590. " /gopath/src/gopkg.in/yaml.v2/yaml.go:153 +0xc6",
  591. "reflect.Value.assignTo(0x570860, 0xc20803f3e0, 0x15)",
  592. " /goroot/src/reflect/value.go:2125 +0x368",
  593. "main.main()",
  594. " /gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go:428 +0x27",
  595. "",
  596. }
  597. c, err := ParseDump(bytes.NewBufferString(strings.Join(data, "\r\n")), ioutil.Discard, false)
  598. if err != nil {
  599. t.Fatal(err)
  600. }
  601. expected := []*Goroutine{
  602. {
  603. Signature: Signature{
  604. State: "running",
  605. Stack: Stack{
  606. Calls: []Call{
  607. {
  608. SrcPath: "??",
  609. Func: Func{Raw: "github.com/cockroachdb/cockroach/storage/engine._Cfunc_DBIterSeek"},
  610. },
  611. {
  612. SrcPath: "/gopath/src/gopkg.in/yaml.v2/yaml.go",
  613. Line: 153,
  614. Func: Func{Raw: "gopkg.in/yaml%2ev2.handleErr"},
  615. Args: Args{Values: []Arg{{Value: 0xc208033b20}}},
  616. },
  617. {
  618. SrcPath: "/goroot/src/reflect/value.go",
  619. Line: 2125,
  620. Func: Func{Raw: "reflect.Value.assignTo"},
  621. Args: Args{Values: []Arg{{Value: 0x570860}, {Value: 0xc20803f3e0}, {Value: 0x15}}},
  622. },
  623. {
  624. SrcPath: "/gopath/src/notabug.org/themusicgod1/panicparse/stack/stack.go",
  625. Line: 428,
  626. Func: Func{Raw: "main.main"},
  627. },
  628. },
  629. },
  630. },
  631. ID: 1,
  632. First: true,
  633. },
  634. }
  635. compareGoroutines(t, expected, c.Goroutines)
  636. }
  637. //
  638. func compareErr(t *testing.T, expected, actual error) {
  639. if expected.Error() != actual.Error() {
  640. t.Fatalf("%v != %v", expected, actual)
  641. }
  642. }
  643. func compareGoroutines(t *testing.T, expected, actual []*Goroutine) {
  644. if len(expected) != len(actual) {
  645. t.Fatalf("Different []*Goroutine length:\n- %v\n- %v", expected, actual)
  646. }
  647. for i := range expected {
  648. if !reflect.DeepEqual(expected[i], actual[i]) {
  649. t.Fatalf("Different Goroutine:\n- %v\n- %v", expected[i], actual[i])
  650. }
  651. }
  652. }
  653. func compareString(t *testing.T, expected, actual string) {
  654. if expected != actual {
  655. t.Fatalf("%q != %q", expected, actual)
  656. }
  657. }