Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 

336 righe
7.1 KiB

  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 graph
  15. import (
  16. "bytes"
  17. "flag"
  18. "fmt"
  19. "io/ioutil"
  20. "path/filepath"
  21. "reflect"
  22. "strconv"
  23. "strings"
  24. "testing"
  25. "github.com/google/pprof/internal/proftest"
  26. )
  27. var updateFlag = flag.Bool("update", false, "Update the golden files")
  28. func TestComposeWithStandardGraph(t *testing.T) {
  29. g := baseGraph()
  30. a, c := baseAttrsAndConfig()
  31. var buf bytes.Buffer
  32. ComposeDot(&buf, g, a, c)
  33. compareGraphs(t, buf.Bytes(), "compose1.dot")
  34. }
  35. func TestComposeWithNodeAttributesAndZeroFlat(t *testing.T) {
  36. g := baseGraph()
  37. a, c := baseAttrsAndConfig()
  38. // Set NodeAttributes for Node 1.
  39. a.Nodes[g.Nodes[0]] = &DotNodeAttributes{
  40. Shape: "folder",
  41. Bold: true,
  42. Peripheries: 2,
  43. URL: "www.google.com",
  44. Formatter: func(ni *NodeInfo) string {
  45. return strings.ToUpper(ni.Name)
  46. },
  47. }
  48. // Set Flat value to zero on Node 2.
  49. g.Nodes[1].Flat = 0
  50. var buf bytes.Buffer
  51. ComposeDot(&buf, g, a, c)
  52. compareGraphs(t, buf.Bytes(), "compose2.dot")
  53. }
  54. func TestComposeWithTagsAndResidualEdge(t *testing.T) {
  55. g := baseGraph()
  56. a, c := baseAttrsAndConfig()
  57. // Add tags to Node 1.
  58. g.Nodes[0].LabelTags["a"] = &Tag{
  59. Name: "tag1",
  60. Cum: 10,
  61. Flat: 10,
  62. }
  63. g.Nodes[0].NumericTags[""] = TagMap{
  64. "b": &Tag{
  65. Name: "tag2",
  66. Cum: 20,
  67. Flat: 20,
  68. Unit: "ms",
  69. },
  70. }
  71. // Set edge to be Residual.
  72. g.Nodes[0].Out[g.Nodes[1]].Residual = true
  73. var buf bytes.Buffer
  74. ComposeDot(&buf, g, a, c)
  75. compareGraphs(t, buf.Bytes(), "compose3.dot")
  76. }
  77. func TestComposeWithNestedTags(t *testing.T) {
  78. g := baseGraph()
  79. a, c := baseAttrsAndConfig()
  80. // Add tags to Node 1.
  81. g.Nodes[0].LabelTags["tag1"] = &Tag{
  82. Name: "tag1",
  83. Cum: 10,
  84. Flat: 10,
  85. }
  86. g.Nodes[0].NumericTags["tag1"] = TagMap{
  87. "tag2": &Tag{
  88. Name: "tag2",
  89. Cum: 20,
  90. Flat: 20,
  91. Unit: "ms",
  92. },
  93. }
  94. var buf bytes.Buffer
  95. ComposeDot(&buf, g, a, c)
  96. compareGraphs(t, buf.Bytes(), "compose5.dot")
  97. }
  98. func TestComposeWithEmptyGraph(t *testing.T) {
  99. g := &Graph{}
  100. a, c := baseAttrsAndConfig()
  101. var buf bytes.Buffer
  102. ComposeDot(&buf, g, a, c)
  103. compareGraphs(t, buf.Bytes(), "compose4.dot")
  104. }
  105. func TestComposeWithStandardGraphAndURL(t *testing.T) {
  106. g := baseGraph()
  107. a, c := baseAttrsAndConfig()
  108. c.LegendURL = "http://example.com"
  109. var buf bytes.Buffer
  110. ComposeDot(&buf, g, a, c)
  111. compareGraphs(t, buf.Bytes(), "compose6.dot")
  112. }
  113. func baseGraph() *Graph {
  114. src := &Node{
  115. Info: NodeInfo{Name: "src"},
  116. Flat: 10,
  117. Cum: 25,
  118. In: make(EdgeMap),
  119. Out: make(EdgeMap),
  120. LabelTags: make(TagMap),
  121. NumericTags: make(map[string]TagMap),
  122. }
  123. dest := &Node{
  124. Info: NodeInfo{Name: "dest"},
  125. Flat: 15,
  126. Cum: 25,
  127. In: make(EdgeMap),
  128. Out: make(EdgeMap),
  129. LabelTags: make(TagMap),
  130. NumericTags: make(map[string]TagMap),
  131. }
  132. edge := &Edge{
  133. Src: src,
  134. Dest: dest,
  135. Weight: 10,
  136. }
  137. src.Out[dest] = edge
  138. src.In[src] = edge
  139. return &Graph{
  140. Nodes: Nodes{
  141. src,
  142. dest,
  143. },
  144. }
  145. }
  146. func baseAttrsAndConfig() (*DotAttributes, *DotConfig) {
  147. a := &DotAttributes{
  148. Nodes: make(map[*Node]*DotNodeAttributes),
  149. }
  150. c := &DotConfig{
  151. Title: "testtitle",
  152. Labels: []string{"label1", "label2"},
  153. Total: 100,
  154. FormatValue: func(v int64) string {
  155. return strconv.FormatInt(v, 10)
  156. },
  157. }
  158. return a, c
  159. }
  160. func compareGraphs(t *testing.T, got []byte, wantFile string) {
  161. wantFile = filepath.Join("testdata", wantFile)
  162. want, err := ioutil.ReadFile(wantFile)
  163. if err != nil {
  164. t.Fatalf("error reading test file %s: %v", wantFile, err)
  165. }
  166. if string(got) != string(want) {
  167. d, err := proftest.Diff(got, want)
  168. if err != nil {
  169. t.Fatalf("error finding diff: %v", err)
  170. }
  171. t.Errorf("Compose incorrectly wrote %s", string(d))
  172. if *updateFlag {
  173. err := ioutil.WriteFile(wantFile, got, 0644)
  174. if err != nil {
  175. t.Errorf("failed to update the golden file %q: %v", wantFile, err)
  176. }
  177. }
  178. }
  179. }
  180. func TestNodeletCountCapping(t *testing.T) {
  181. labelTags := make(TagMap)
  182. for i := 0; i < 10; i++ {
  183. name := fmt.Sprintf("tag-%d", i)
  184. labelTags[name] = &Tag{
  185. Name: name,
  186. Flat: 10,
  187. Cum: 10,
  188. }
  189. }
  190. numTags := make(TagMap)
  191. for i := 0; i < 10; i++ {
  192. name := fmt.Sprintf("num-tag-%d", i)
  193. numTags[name] = &Tag{
  194. Name: name,
  195. Unit: "mb",
  196. Value: 16,
  197. Flat: 10,
  198. Cum: 10,
  199. }
  200. }
  201. node1 := &Node{
  202. Info: NodeInfo{Name: "node1-with-tags"},
  203. Flat: 10,
  204. Cum: 10,
  205. NumericTags: map[string]TagMap{"": numTags},
  206. LabelTags: labelTags,
  207. }
  208. node2 := &Node{
  209. Info: NodeInfo{Name: "node2"},
  210. Flat: 15,
  211. Cum: 15,
  212. }
  213. node3 := &Node{
  214. Info: NodeInfo{Name: "node3"},
  215. Flat: 15,
  216. Cum: 15,
  217. }
  218. g := &Graph{
  219. Nodes: Nodes{
  220. node1,
  221. node2,
  222. node3,
  223. },
  224. }
  225. for n := 1; n <= 3; n++ {
  226. input := maxNodelets + n
  227. if got, want := len(g.SelectTopNodes(input, true)), n; got != want {
  228. t.Errorf("SelectTopNodes(%d): got %d nodes, want %d", input, got, want)
  229. }
  230. }
  231. }
  232. func TestMultilinePrintableName(t *testing.T) {
  233. ni := &NodeInfo{
  234. Name: "test1.test2::test3",
  235. File: "src/file.cc",
  236. Address: 123,
  237. Lineno: 999,
  238. }
  239. want := fmt.Sprintf(`%016x\ntest1\ntest2\ntest3\nfile.cc:999\n`, 123)
  240. if got := multilinePrintableName(ni); got != want {
  241. t.Errorf("multilinePrintableName(%#v) == %q, want %q", ni, got, want)
  242. }
  243. }
  244. func TestTagCollapse(t *testing.T) {
  245. makeTag := func(name, unit string, value, flat, cum int64) *Tag {
  246. return &Tag{name, unit, value, flat, 0, cum, 0}
  247. }
  248. tagSource := []*Tag{
  249. makeTag("12mb", "mb", 12, 100, 100),
  250. makeTag("1kb", "kb", 1, 1, 1),
  251. makeTag("1mb", "mb", 1, 1000, 1000),
  252. makeTag("2048mb", "mb", 2048, 1000, 1000),
  253. makeTag("1b", "b", 1, 100, 100),
  254. makeTag("2b", "b", 2, 100, 100),
  255. makeTag("7b", "b", 7, 100, 100),
  256. }
  257. tagWant := [][]*Tag{
  258. {
  259. makeTag("1B..2GB", "", 0, 2401, 2401),
  260. },
  261. {
  262. makeTag("2GB", "", 0, 1000, 1000),
  263. makeTag("1B..12MB", "", 0, 1401, 1401),
  264. },
  265. {
  266. makeTag("2GB", "", 0, 1000, 1000),
  267. makeTag("12MB", "", 0, 100, 100),
  268. makeTag("1B..1MB", "", 0, 1301, 1301),
  269. },
  270. {
  271. makeTag("2GB", "", 0, 1000, 1000),
  272. makeTag("1MB", "", 0, 1000, 1000),
  273. makeTag("2B..1kB", "", 0, 201, 201),
  274. makeTag("1B", "", 0, 100, 100),
  275. makeTag("12MB", "", 0, 100, 100),
  276. },
  277. }
  278. for _, tc := range tagWant {
  279. var got, want []*Tag
  280. b := builder{nil, &DotAttributes{}, &DotConfig{}}
  281. got = b.collapsedTags(tagSource, len(tc), true)
  282. want = SortTags(tc, true)
  283. if !reflect.DeepEqual(got, want) {
  284. t.Errorf("collapse to %d, got:\n%v\nwant:\n%v", len(tc), tagString(got), tagString(want))
  285. }
  286. }
  287. }
  288. func tagString(t []*Tag) string {
  289. var ret []string
  290. for _, s := range t {
  291. ret = append(ret, fmt.Sprintln(s))
  292. }
  293. return strings.Join(ret, ":")
  294. }