Nenhuma descrição

report_test.go 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. // Copyright 2014 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package report
  15. import (
  16. "bytes"
  17. "io/ioutil"
  18. "regexp"
  19. "runtime"
  20. "testing"
  21. "github.com/google/pprof/internal/binutils"
  22. "github.com/google/pprof/internal/graph"
  23. "github.com/google/pprof/internal/proftest"
  24. "github.com/google/pprof/profile"
  25. )
  26. type testcase struct {
  27. rpt *Report
  28. want string
  29. }
  30. func TestSource(t *testing.T) {
  31. const path = "testdata/"
  32. sampleValue1 := func(v []int64) int64 {
  33. return v[1]
  34. }
  35. for _, tc := range []testcase{
  36. {
  37. rpt: New(
  38. testProfile.Copy(),
  39. &Options{
  40. OutputFormat: List,
  41. Symbol: regexp.MustCompile(`.`),
  42. TrimPath: "/some/path",
  43. SampleValue: sampleValue1,
  44. SampleUnit: testProfile.SampleType[1].Unit,
  45. },
  46. ),
  47. want: path + "source.rpt",
  48. },
  49. {
  50. rpt: New(
  51. testProfile.Copy(),
  52. &Options{
  53. OutputFormat: Dot,
  54. CallTree: true,
  55. Symbol: regexp.MustCompile(`.`),
  56. TrimPath: "/some/path",
  57. SampleValue: sampleValue1,
  58. SampleUnit: testProfile.SampleType[1].Unit,
  59. },
  60. ),
  61. want: path + "source.dot",
  62. },
  63. } {
  64. var b bytes.Buffer
  65. if err := Generate(&b, tc.rpt, &binutils.Binutils{}); err != nil {
  66. t.Fatalf("%s: %v", tc.want, err)
  67. }
  68. gold, err := ioutil.ReadFile(tc.want)
  69. if err != nil {
  70. t.Fatalf("%s: %v", tc.want, err)
  71. }
  72. if runtime.GOOS == "windows" {
  73. gold = bytes.Replace(gold, []byte("testdata/"), []byte("testdata\\"), -1)
  74. }
  75. if string(b.String()) != string(gold) {
  76. d, err := proftest.Diff(gold, b.Bytes())
  77. if err != nil {
  78. t.Fatalf("%s: %v", "source", err)
  79. }
  80. t.Error("source" + "\n" + string(d) + "\n" + "gold:\n" + tc.want)
  81. }
  82. }
  83. }
  84. var testM = []*profile.Mapping{
  85. {
  86. ID: 1,
  87. HasFunctions: true,
  88. HasFilenames: true,
  89. HasLineNumbers: true,
  90. HasInlineFrames: true,
  91. },
  92. }
  93. var testF = []*profile.Function{
  94. {
  95. ID: 1,
  96. Name: "main",
  97. Filename: "testdata/source1",
  98. },
  99. {
  100. ID: 2,
  101. Name: "foo",
  102. Filename: "testdata/source1",
  103. },
  104. {
  105. ID: 3,
  106. Name: "bar",
  107. Filename: "testdata/source1",
  108. },
  109. {
  110. ID: 4,
  111. Name: "tee",
  112. Filename: "/some/path/testdata/source2",
  113. },
  114. }
  115. var testL = []*profile.Location{
  116. {
  117. ID: 1,
  118. Mapping: testM[0],
  119. Line: []profile.Line{
  120. {
  121. Function: testF[0],
  122. Line: 2,
  123. },
  124. },
  125. },
  126. {
  127. ID: 2,
  128. Mapping: testM[0],
  129. Line: []profile.Line{
  130. {
  131. Function: testF[1],
  132. Line: 4,
  133. },
  134. },
  135. },
  136. {
  137. ID: 3,
  138. Mapping: testM[0],
  139. Line: []profile.Line{
  140. {
  141. Function: testF[2],
  142. Line: 10,
  143. },
  144. },
  145. },
  146. {
  147. ID: 4,
  148. Mapping: testM[0],
  149. Line: []profile.Line{
  150. {
  151. Function: testF[3],
  152. Line: 2,
  153. },
  154. },
  155. },
  156. {
  157. ID: 5,
  158. Mapping: testM[0],
  159. Line: []profile.Line{
  160. {
  161. Function: testF[3],
  162. Line: 8,
  163. },
  164. },
  165. },
  166. }
  167. var testProfile = &profile.Profile{
  168. PeriodType: &profile.ValueType{Type: "cpu", Unit: "millisecond"},
  169. Period: 10,
  170. DurationNanos: 10e9,
  171. SampleType: []*profile.ValueType{
  172. {Type: "samples", Unit: "count"},
  173. {Type: "cpu", Unit: "cycles"},
  174. },
  175. Sample: []*profile.Sample{
  176. {
  177. Location: []*profile.Location{testL[0]},
  178. Value: []int64{1, 1},
  179. },
  180. {
  181. Location: []*profile.Location{testL[2], testL[1], testL[0]},
  182. Value: []int64{1, 10},
  183. },
  184. {
  185. Location: []*profile.Location{testL[4], testL[2], testL[0]},
  186. Value: []int64{1, 100},
  187. },
  188. {
  189. Location: []*profile.Location{testL[3], testL[0]},
  190. Value: []int64{1, 1000},
  191. },
  192. {
  193. Location: []*profile.Location{testL[4], testL[3], testL[0]},
  194. Value: []int64{1, 10000},
  195. },
  196. },
  197. Location: testL,
  198. Function: testF,
  199. Mapping: testM,
  200. }
  201. func TestDisambiguation(t *testing.T) {
  202. parent1 := &graph.Node{Info: graph.NodeInfo{Name: "parent1"}}
  203. parent2 := &graph.Node{Info: graph.NodeInfo{Name: "parent2"}}
  204. child1 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent1}
  205. child2 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent2}
  206. child3 := &graph.Node{Info: graph.NodeInfo{Name: "child"}, Function: parent1}
  207. sibling := &graph.Node{Info: graph.NodeInfo{Name: "sibling"}, Function: parent1}
  208. n := []*graph.Node{parent1, parent2, child1, child2, child3, sibling}
  209. wanted := map[*graph.Node]string{
  210. parent1: "parent1",
  211. parent2: "parent2",
  212. child1: "child [1/2]",
  213. child2: "child [2/2]",
  214. child3: "child [1/2]",
  215. sibling: "sibling",
  216. }
  217. g := &graph.Graph{Nodes: n}
  218. names := getDisambiguatedNames(g)
  219. for node, want := range wanted {
  220. if got := names[node]; got != want {
  221. t.Errorf("name %s, got %s, want %s", node.Info.Name, got, want)
  222. }
  223. }
  224. }
  225. func TestFunctionMap(t *testing.T) {
  226. fm := make(functionMap)
  227. nodes := []graph.NodeInfo{
  228. {Name: "fun1"},
  229. {Name: "fun2", File: "filename"},
  230. {Name: "fun1"},
  231. {Name: "fun2", File: "filename2"},
  232. }
  233. want := []profile.Function{
  234. {ID: 1, Name: "fun1"},
  235. {ID: 2, Name: "fun2", Filename: "filename"},
  236. {ID: 1, Name: "fun1"},
  237. {ID: 3, Name: "fun2", Filename: "filename2"},
  238. }
  239. for i, tc := range nodes {
  240. if got, want := fm.FindOrAdd(tc), want[i]; *got != want {
  241. t.Errorf("%d: want %v, got %v", i, want, got)
  242. }
  243. }
  244. }
  245. func TestLegendActiveFilters(t *testing.T) {
  246. activeFilterInput := []string{
  247. "focus=123|456|789|101112|131415|161718|192021|222324|252627|282930|313233|343536|363738|acbdefghijklmnop",
  248. "show=short filter",
  249. }
  250. expectedLegendActiveFilter := []string{
  251. "Active filters:",
  252. " focus=123|456|789|101112|131415|161718|192021|222324|252627|282930|313233|343536…",
  253. " show=short filter",
  254. }
  255. legendActiveFilter := legendActiveFilters(activeFilterInput)
  256. if len(legendActiveFilter) != len(expectedLegendActiveFilter) {
  257. t.Errorf("wanted length %v got length %v", len(expectedLegendActiveFilter), len(legendActiveFilter))
  258. }
  259. for i := range legendActiveFilter {
  260. if legendActiveFilter[i] != expectedLegendActiveFilter[i] {
  261. t.Errorf("%d: want \"%v\", got \"%v\"", i, expectedLegendActiveFilter[i], legendActiveFilter[i])
  262. }
  263. }
  264. }
  265. func TestComputeTotal(t *testing.T) {
  266. p1 := testProfile.Copy()
  267. p1.Sample = []*profile.Sample{
  268. {
  269. Location: []*profile.Location{testL[0]},
  270. Value: []int64{1, 1},
  271. },
  272. {
  273. Location: []*profile.Location{testL[2], testL[1], testL[0]},
  274. Value: []int64{1, 10},
  275. },
  276. {
  277. Location: []*profile.Location{testL[4], testL[2], testL[0]},
  278. Value: []int64{1, 100},
  279. },
  280. }
  281. p2 := testProfile.Copy()
  282. p2.Sample = []*profile.Sample{
  283. {
  284. Location: []*profile.Location{testL[0]},
  285. Value: []int64{1, 1},
  286. },
  287. {
  288. Location: []*profile.Location{testL[2], testL[1], testL[0]},
  289. Value: []int64{1, -10},
  290. },
  291. {
  292. Location: []*profile.Location{testL[4], testL[2], testL[0]},
  293. Value: []int64{1, 100},
  294. },
  295. }
  296. p3 := testProfile.Copy()
  297. p3.Sample = []*profile.Sample{
  298. {
  299. Location: []*profile.Location{testL[0]},
  300. Value: []int64{10000, 1},
  301. },
  302. {
  303. Location: []*profile.Location{testL[2], testL[1], testL[0]},
  304. Value: []int64{-10, 3},
  305. Label: map[string][]string{"pprof::base": {"true"}},
  306. },
  307. {
  308. Location: []*profile.Location{testL[2], testL[1], testL[0]},
  309. Value: []int64{1000, -10},
  310. },
  311. {
  312. Location: []*profile.Location{testL[2], testL[1], testL[0]},
  313. Value: []int64{-9000, 3},
  314. Label: map[string][]string{"pprof::base": {"true"}},
  315. },
  316. {
  317. Location: []*profile.Location{testL[2], testL[1], testL[0]},
  318. Value: []int64{-1, 3},
  319. Label: map[string][]string{"pprof::base": {"true"}},
  320. },
  321. {
  322. Location: []*profile.Location{testL[4], testL[2], testL[0]},
  323. Value: []int64{100, 100},
  324. },
  325. {
  326. Location: []*profile.Location{testL[2], testL[1], testL[0]},
  327. Value: []int64{100, 3},
  328. Label: map[string][]string{"pprof::base": {"true"}},
  329. },
  330. }
  331. testcases := []struct {
  332. desc string
  333. prof *profile.Profile
  334. value, meanDiv func(v []int64) int64
  335. wantTotal int64
  336. }{
  337. {
  338. desc: "no diff base, all positive values, index 1",
  339. prof: p1,
  340. value: func(v []int64) int64 {
  341. return v[0]
  342. },
  343. wantTotal: 3,
  344. },
  345. {
  346. desc: "no diff base, all positive values, index 2",
  347. prof: p1,
  348. value: func(v []int64) int64 {
  349. return v[1]
  350. },
  351. wantTotal: 111,
  352. },
  353. {
  354. desc: "no diff base, some negative values",
  355. prof: p2,
  356. value: func(v []int64) int64 {
  357. return v[1]
  358. },
  359. wantTotal: 111,
  360. },
  361. {
  362. desc: "diff base, some negative values",
  363. prof: p3,
  364. value: func(v []int64) int64 {
  365. return v[0]
  366. },
  367. wantTotal: 9111,
  368. },
  369. }
  370. for _, tc := range testcases {
  371. t.Run(tc.desc, func(t *testing.T) {
  372. if gotTotal := computeTotal(tc.prof, tc.value, tc.meanDiv); gotTotal != tc.wantTotal {
  373. t.Errorf("got total %d, want %v", gotTotal, tc.wantTotal)
  374. }
  375. })
  376. }
  377. }