暂无描述

graph_test.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. package graph
  2. import (
  3. "fmt"
  4. "testing"
  5. "github.com/google/pprof/profile"
  6. )
  7. func edgeDebugString(edge *Edge) string {
  8. debug := ""
  9. debug += fmt.Sprintf("\t\tSrc: %p\n", edge.Src)
  10. debug += fmt.Sprintf("\t\tDest: %p\n", edge.Dest)
  11. debug += fmt.Sprintf("\t\tWeight: %d\n", edge.Weight)
  12. debug += fmt.Sprintf("\t\tResidual: %t\n", edge.Residual)
  13. debug += fmt.Sprintf("\t\tInline: %t\n", edge.Inline)
  14. return debug
  15. }
  16. func edgeMapsDebugString(in, out EdgeMap) string {
  17. debug := ""
  18. debug += "In Edges:\n"
  19. for parent, edge := range in {
  20. debug += fmt.Sprintf("\tParent: %p\n", parent)
  21. debug += edgeDebugString(edge)
  22. }
  23. debug += "Out Edges:\n"
  24. for child, edge := range out {
  25. debug += fmt.Sprintf("\tChild: %p\n", child)
  26. debug += edgeDebugString(edge)
  27. }
  28. return debug
  29. }
  30. func graphDebugString(graph *Graph) string {
  31. debug := ""
  32. for i, node := range graph.Nodes {
  33. debug += fmt.Sprintf("Node %d: %p\n", i, node)
  34. }
  35. for i, node := range graph.Nodes {
  36. debug += "\n"
  37. debug += fmt.Sprintf("=== Node %d: %p ===\n", i, node)
  38. debug += edgeMapsDebugString(node.In, node.Out)
  39. }
  40. return debug
  41. }
  42. func expectedNodesDebugString(expected []expectedNode) string {
  43. debug := ""
  44. for i, node := range expected {
  45. debug += fmt.Sprintf("Node %d: %p\n", i, node.node)
  46. }
  47. for i, node := range expected {
  48. debug += "\n"
  49. debug += fmt.Sprintf("=== Node %d: %p ===\n", i, node.node)
  50. debug += edgeMapsDebugString(node.in, node.out)
  51. }
  52. return debug
  53. }
  54. // edgeMapsEqual checks if all the edges in this equal all the edges in that.
  55. func edgeMapsEqual(this, that EdgeMap) bool {
  56. if len(this) != len(that) {
  57. return false
  58. }
  59. for node, thisEdge := range this {
  60. if *thisEdge != *that[node] {
  61. return false
  62. }
  63. }
  64. return true
  65. }
  66. // nodesEqual checks if node is equal to expected.
  67. func nodesEqual(node *Node, expected expectedNode) bool {
  68. return node == expected.node && edgeMapsEqual(node.In, expected.in) &&
  69. edgeMapsEqual(node.Out, expected.out)
  70. }
  71. // graphsEqual checks if graph is equivalent to the graph templated by expected.
  72. func graphsEqual(graph *Graph, expected []expectedNode) bool {
  73. if len(graph.Nodes) != len(expected) {
  74. return false
  75. }
  76. expectedSet := make(map[*Node]expectedNode)
  77. for i := range expected {
  78. expectedSet[expected[i].node] = expected[i]
  79. }
  80. for _, node := range graph.Nodes {
  81. expectedNode, found := expectedSet[node]
  82. if !found || !nodesEqual(node, expectedNode) {
  83. return false
  84. }
  85. }
  86. return true
  87. }
  88. type expectedNode struct {
  89. node *Node
  90. in, out EdgeMap
  91. }
  92. type trimTreeTestcase struct {
  93. initial *Graph
  94. expected []expectedNode
  95. keep NodePtrSet
  96. }
  97. // makeExpectedEdgeResidual makes the edge from parent to child residual.
  98. func makeExpectedEdgeResidual(parent, child expectedNode) {
  99. parent.out[child.node].Residual = true
  100. child.in[parent.node].Residual = true
  101. }
  102. func makeEdgeInline(edgeMap EdgeMap, node *Node) {
  103. edgeMap[node].Inline = true
  104. }
  105. func setEdgeWeight(edgeMap EdgeMap, node *Node, weight int64) {
  106. edgeMap[node].Weight = weight
  107. }
  108. // createEdges creates directed edges from the parent to each of the children.
  109. func createEdges(parent *Node, children ...*Node) {
  110. for _, child := range children {
  111. edge := &Edge{
  112. Src: parent,
  113. Dest: child,
  114. }
  115. parent.Out[child] = edge
  116. child.In[parent] = edge
  117. }
  118. }
  119. // createEmptyNode creates a node without any edges.
  120. func createEmptyNode() *Node {
  121. return &Node{
  122. In: make(EdgeMap),
  123. Out: make(EdgeMap),
  124. }
  125. }
  126. // createExpectedNodes creates a slice of expectedNodes from nodes.
  127. func createExpectedNodes(nodes ...*Node) ([]expectedNode, NodePtrSet) {
  128. expected := make([]expectedNode, len(nodes))
  129. keep := make(NodePtrSet, len(nodes))
  130. for i, node := range nodes {
  131. expected[i] = expectedNode{
  132. node: node,
  133. in: make(EdgeMap),
  134. out: make(EdgeMap),
  135. }
  136. keep[node] = true
  137. }
  138. return expected, keep
  139. }
  140. // createExpectedEdges creates directed edges from the parent to each of the
  141. // children.
  142. func createExpectedEdges(parent expectedNode, children ...expectedNode) {
  143. for _, child := range children {
  144. edge := &Edge{
  145. Src: parent.node,
  146. Dest: child.node,
  147. }
  148. parent.out[child.node] = edge
  149. child.in[parent.node] = edge
  150. }
  151. }
  152. // createTestCase1 creates a test case that initially looks like:
  153. // 0
  154. // |(5)
  155. // 1
  156. // (3)/ \(4)
  157. // 2 3.
  158. //
  159. // After keeping 0, 2, and 3, it expects the graph:
  160. // 0
  161. // (3)/ \(4)
  162. // 2 3.
  163. func createTestCase1() trimTreeTestcase {
  164. // Create initial graph
  165. graph := &Graph{make(Nodes, 4)}
  166. nodes := graph.Nodes
  167. for i := range nodes {
  168. nodes[i] = createEmptyNode()
  169. }
  170. createEdges(nodes[0], nodes[1])
  171. createEdges(nodes[1], nodes[2], nodes[3])
  172. makeEdgeInline(nodes[0].Out, nodes[1])
  173. makeEdgeInline(nodes[1].Out, nodes[2])
  174. setEdgeWeight(nodes[0].Out, nodes[1], 5)
  175. setEdgeWeight(nodes[1].Out, nodes[2], 3)
  176. setEdgeWeight(nodes[1].Out, nodes[3], 4)
  177. // Create expected graph
  178. expected, keep := createExpectedNodes(nodes[0], nodes[2], nodes[3])
  179. createExpectedEdges(expected[0], expected[1], expected[2])
  180. makeEdgeInline(expected[0].out, expected[1].node)
  181. makeExpectedEdgeResidual(expected[0], expected[1])
  182. makeExpectedEdgeResidual(expected[0], expected[2])
  183. setEdgeWeight(expected[0].out, expected[1].node, 3)
  184. setEdgeWeight(expected[0].out, expected[2].node, 4)
  185. return trimTreeTestcase{
  186. initial: graph,
  187. expected: expected,
  188. keep: keep,
  189. }
  190. }
  191. // createTestCase2 creates a test case that initially looks like:
  192. // 3
  193. // | (12)
  194. // 1
  195. // | (8)
  196. // 2
  197. // | (15)
  198. // 0
  199. // | (10)
  200. // 4.
  201. //
  202. // After keeping 3 and 4, it expects the graph:
  203. // 3
  204. // | (10)
  205. // 4.
  206. func createTestCase2() trimTreeTestcase {
  207. // Create initial graph
  208. graph := &Graph{make(Nodes, 5)}
  209. nodes := graph.Nodes
  210. for i := range nodes {
  211. nodes[i] = createEmptyNode()
  212. }
  213. createEdges(nodes[3], nodes[1])
  214. createEdges(nodes[1], nodes[2])
  215. createEdges(nodes[2], nodes[0])
  216. createEdges(nodes[0], nodes[4])
  217. setEdgeWeight(nodes[3].Out, nodes[1], 12)
  218. setEdgeWeight(nodes[1].Out, nodes[2], 8)
  219. setEdgeWeight(nodes[2].Out, nodes[0], 15)
  220. setEdgeWeight(nodes[0].Out, nodes[4], 10)
  221. // Create expected graph
  222. expected, keep := createExpectedNodes(nodes[3], nodes[4])
  223. createExpectedEdges(expected[0], expected[1])
  224. makeExpectedEdgeResidual(expected[0], expected[1])
  225. setEdgeWeight(expected[0].out, expected[1].node, 10)
  226. return trimTreeTestcase{
  227. initial: graph,
  228. expected: expected,
  229. keep: keep,
  230. }
  231. }
  232. // createTestCase3 creates an initially empty graph and expects an empty graph
  233. // after trimming.
  234. func createTestCase3() trimTreeTestcase {
  235. graph := &Graph{make(Nodes, 0)}
  236. expected, keep := createExpectedNodes()
  237. return trimTreeTestcase{
  238. initial: graph,
  239. expected: expected,
  240. keep: keep,
  241. }
  242. }
  243. // createTestCase4 creates a test case that initially looks like:
  244. // 0.
  245. //
  246. // After keeping 0, it expects the graph:
  247. // 0.
  248. func createTestCase4() trimTreeTestcase {
  249. graph := &Graph{make(Nodes, 1)}
  250. nodes := graph.Nodes
  251. for i := range nodes {
  252. nodes[i] = createEmptyNode()
  253. }
  254. expected, keep := createExpectedNodes(nodes[0])
  255. return trimTreeTestcase{
  256. initial: graph,
  257. expected: expected,
  258. keep: keep,
  259. }
  260. }
  261. func createTrimTreeTestCases() []trimTreeTestcase {
  262. caseGenerators := []func() trimTreeTestcase{
  263. createTestCase1,
  264. createTestCase2,
  265. createTestCase3,
  266. createTestCase4,
  267. }
  268. cases := make([]trimTreeTestcase, len(caseGenerators))
  269. for i, gen := range caseGenerators {
  270. cases[i] = gen()
  271. }
  272. return cases
  273. }
  274. func TestTrimTree(t *testing.T) {
  275. tests := createTrimTreeTestCases()
  276. for _, test := range tests {
  277. graph := test.initial
  278. graph.TrimTree(test.keep)
  279. if !graphsEqual(graph, test.expected) {
  280. t.Fatalf("Graphs do not match.\nExpected: %s\nFound: %s\n",
  281. expectedNodesDebugString(test.expected),
  282. graphDebugString(graph))
  283. }
  284. }
  285. }
  286. func nodeTestProfile() *profile.Profile {
  287. mappings := []*profile.Mapping{
  288. {
  289. ID: 1,
  290. File: "symbolized_binary",
  291. },
  292. {
  293. ID: 2,
  294. File: "unsymbolized_library_1",
  295. },
  296. {
  297. ID: 3,
  298. File: "unsymbolized_library_2",
  299. },
  300. }
  301. functions := []*profile.Function{
  302. {ID: 1, Name: "symname"},
  303. {ID: 2},
  304. }
  305. locations := []*profile.Location{
  306. {
  307. ID: 1,
  308. Mapping: mappings[0],
  309. Line: []profile.Line{
  310. {Function: functions[0]},
  311. },
  312. },
  313. {
  314. ID: 2,
  315. Mapping: mappings[1],
  316. Line: []profile.Line{
  317. {Function: functions[1]},
  318. },
  319. },
  320. {
  321. ID: 3,
  322. Mapping: mappings[2],
  323. },
  324. }
  325. return &profile.Profile{
  326. PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
  327. SampleType: []*profile.ValueType{
  328. {Type: "type", Unit: "unit"},
  329. },
  330. Sample: []*profile.Sample{
  331. {
  332. Location: []*profile.Location{locations[0]},
  333. Value: []int64{1},
  334. },
  335. {
  336. Location: []*profile.Location{locations[1]},
  337. Value: []int64{1},
  338. },
  339. {
  340. Location: []*profile.Location{locations[2]},
  341. Value: []int64{1},
  342. },
  343. },
  344. Location: locations,
  345. Function: functions,
  346. Mapping: mappings,
  347. }
  348. }
  349. // Check that nodes are properly created for a simple profile.
  350. func TestCreateNodes(t *testing.T) {
  351. testProfile := nodeTestProfile()
  352. wantNodeSet := NodeSet{
  353. {Name: "symname"}: true,
  354. {Objfile: "unsymbolized_library_1"}: true,
  355. {Objfile: "unsymbolized_library_2"}: true,
  356. }
  357. nodes, _ := CreateNodes(testProfile, &Options{})
  358. if len(nodes) != len(wantNodeSet) {
  359. t.Errorf("got %d nodes, want %d", len(nodes), len(wantNodeSet))
  360. }
  361. for _, node := range nodes {
  362. if !wantNodeSet[node.Info] {
  363. t.Errorf("unexpected node %v", node.Info)
  364. }
  365. }
  366. }
  367. func TestShortenFunctionName(t *testing.T) {
  368. type testCase struct {
  369. name string
  370. want string
  371. }
  372. testcases := []testCase{
  373. {
  374. "root",
  375. "root",
  376. },
  377. {
  378. "syscall.Syscall",
  379. "syscall.Syscall",
  380. },
  381. {
  382. "net/http.(*conn).serve",
  383. "http.(*conn).serve",
  384. },
  385. {
  386. "github.com/blahBlah/foo.Foo",
  387. "foo.Foo",
  388. },
  389. {
  390. "github.com/BlahBlah/foo.Foo",
  391. "foo.Foo",
  392. },
  393. {
  394. "github.com/blah-blah/foo_bar.(*FooBar).Foo",
  395. "foo_bar.(*FooBar).Foo",
  396. },
  397. {
  398. "encoding/json.(*structEncoder).(encoding/json.encode)-fm",
  399. "json.(*structEncoder).(encoding/json.encode)-fm",
  400. },
  401. {
  402. "github.com/blah/blah/vendor/gopkg.in/redis.v3.(*baseClient).(github.com/blah/blah/vendor/gopkg.in/redis.v3.process)-fm",
  403. "redis.v3.(*baseClient).(github.com/blah/blah/vendor/gopkg.in/redis.v3.process)-fm",
  404. },
  405. {
  406. "java.util.concurrent.ThreadPoolExecutor$Worker.run",
  407. "ThreadPoolExecutor$Worker.run",
  408. },
  409. {
  410. "java.bar.foo.FooBar.run(java.lang.Runnable)",
  411. "FooBar.run",
  412. },
  413. {
  414. "(anonymous namespace)::Bar::Foo",
  415. "Bar::Foo",
  416. },
  417. {
  418. "(anonymous namespace)::foo",
  419. "foo",
  420. },
  421. {
  422. "foo_bar::Foo::bar",
  423. "Foo::bar",
  424. },
  425. {
  426. "foo",
  427. "foo",
  428. },
  429. }
  430. for _, tc := range testcases {
  431. name := ShortenFunctionName(tc.name)
  432. if got, want := name, tc.want; got != want {
  433. t.Errorf("ShortenFunctionName(%q) = %q, want %q", tc.name, got, want)
  434. }
  435. }
  436. }