1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package profile
19
20 import (
21 "fmt"
22 "sort"
23 "strings"
24 )
25
26
27 type Options struct {
28 SampleValue func(s []int64) int64
29 SampleMeanDivisor func(s []int64) int64
30
31 DropNegative bool
32
33 KeptNodes NodeSet
34 }
35
36
37 type Nodes []*Node
38
39
40
41 type Node struct {
42
43 Info NodeInfo
44
45
46
47
48
49
50 Function *Node
51
52
53
54 Flat, FlatDiv, Cum, CumDiv int64
55
56
57
58 In, Out EdgeMap
59 }
60
61
62
63 type Graph struct {
64 Nodes Nodes
65 }
66
67
68
69 func (n *Node) FlatValue() int64 {
70 if n.FlatDiv == 0 {
71 return n.Flat
72 }
73 return n.Flat / n.FlatDiv
74 }
75
76
77
78 func (n *Node) CumValue() int64 {
79 if n.CumDiv == 0 {
80 return n.Cum
81 }
82 return n.Cum / n.CumDiv
83 }
84
85
86
87 func (n *Node) AddToEdge(to *Node, v int64, residual, inline bool) {
88 n.AddToEdgeDiv(to, 0, v, residual, inline)
89 }
90
91
92
93 func (n *Node) AddToEdgeDiv(to *Node, dv, v int64, residual, inline bool) {
94 if e := n.Out.FindTo(to); e != nil {
95 e.WeightDiv += dv
96 e.Weight += v
97 if residual {
98 e.Residual = true
99 }
100 if !inline {
101 e.Inline = false
102 }
103 return
104 }
105
106 info := &Edge{Src: n, Dest: to, WeightDiv: dv, Weight: v, Residual: residual, Inline: inline}
107 n.Out.Add(info)
108 to.In.Add(info)
109 }
110
111
112 type NodeInfo struct {
113 Name string
114 Address uint64
115 StartLine, Lineno int
116 }
117
118
119 func (i *NodeInfo) PrintableName() string {
120 return strings.Join(i.NameComponents(), " ")
121 }
122
123
124 func (i *NodeInfo) NameComponents() []string {
125 var name []string
126 if i.Address != 0 {
127 name = append(name, fmt.Sprintf("%016x", i.Address))
128 }
129 if fun := i.Name; fun != "" {
130 name = append(name, fun)
131 }
132
133 switch {
134 case i.Lineno != 0:
135
136 name = append(name, fmt.Sprintf(":%d", i.Lineno))
137 case i.Name != "":
138
139 default:
140
141 name = append(name, "<unknown>")
142 }
143 return name
144 }
145
146
147
148 type NodeMap map[NodeInfo]*Node
149
150
151 type NodeSet map[NodeInfo]bool
152
153
154
155
156
157
158
159
160
161 type NodePtrSet map[*Node]bool
162
163
164
165
166 func (nm NodeMap) FindOrInsertNode(info NodeInfo, kept NodeSet) *Node {
167 if kept != nil {
168 if _, ok := kept[info]; !ok {
169 return nil
170 }
171 }
172
173 if n, ok := nm[info]; ok {
174 return n
175 }
176
177 n := &Node{
178 Info: info,
179 }
180 nm[info] = n
181 if info.Address == 0 && info.Lineno == 0 {
182
183
184 n.Function = n
185 return n
186 }
187
188 info.Address = 0
189 info.Lineno = 0
190 n.Function = nm.FindOrInsertNode(info, nil)
191 return n
192 }
193
194
195 type EdgeMap []*Edge
196
197 func (em EdgeMap) FindTo(n *Node) *Edge {
198 for _, e := range em {
199 if e.Dest == n {
200 return e
201 }
202 }
203 return nil
204 }
205
206 func (em *EdgeMap) Add(e *Edge) {
207 *em = append(*em, e)
208 }
209
210 func (em *EdgeMap) Delete(e *Edge) {
211 for i, edge := range *em {
212 if edge == e {
213 (*em)[i] = (*em)[len(*em)-1]
214 *em = (*em)[:len(*em)-1]
215 return
216 }
217 }
218 }
219
220
221 type Edge struct {
222 Src, Dest *Node
223
224 Weight, WeightDiv int64
225
226
227
228 Residual bool
229
230 Inline bool
231 }
232
233
234
235 func (e *Edge) WeightValue() int64 {
236 if e.WeightDiv == 0 {
237 return e.Weight
238 }
239 return e.Weight / e.WeightDiv
240 }
241
242
243 func NewGraph(prof *Profile, o *Options) *Graph {
244 nodes, locationMap := CreateNodes(prof, o)
245 seenNode := make(map[*Node]bool)
246 seenEdge := make(map[nodePair]bool)
247 for _, sample := range prof.Sample {
248 var w, dw int64
249 w = o.SampleValue(sample.Value)
250 if o.SampleMeanDivisor != nil {
251 dw = o.SampleMeanDivisor(sample.Value)
252 }
253 if dw == 0 && w == 0 {
254 continue
255 }
256 clear(seenNode)
257 clear(seenEdge)
258 var parent *Node
259
260 residual := false
261
262
263
264
265
266
267
268
269 i := 1
270 if last := len(sample.Location) - 1; last < i {
271 i = last
272 }
273 for ; i >= 0; i-- {
274 l := sample.Location[i]
275 locNodes := locationMap.get(l.ID)
276 for ni := len(locNodes) - 1; ni >= 0; ni-- {
277 n := locNodes[ni]
278 if n == nil {
279 residual = true
280 continue
281 }
282
283 _, sawNode := seenNode[n]
284 if !sawNode {
285 seenNode[n] = true
286 n.addSample(dw, w, false)
287 }
288
289 if (!sawNode || !seenEdge[nodePair{n, parent}]) && parent != nil && n != parent {
290 seenEdge[nodePair{n, parent}] = true
291 parent.AddToEdgeDiv(n, dw, w, residual, ni != len(locNodes)-1)
292 }
293
294 parent = n
295 residual = false
296 }
297 }
298 if parent != nil && !residual {
299
300 parent.addSample(dw, w, true)
301 }
302 }
303
304 return selectNodesForGraph(nodes, o.DropNegative)
305 }
306
307 func selectNodesForGraph(nodes Nodes, dropNegative bool) *Graph {
308
309 gNodes := make(Nodes, 0, len(nodes))
310 for _, n := range nodes {
311 if n == nil {
312 continue
313 }
314 if n.Cum == 0 && n.Flat == 0 {
315 continue
316 }
317 if dropNegative && isNegative(n) {
318 continue
319 }
320 gNodes = append(gNodes, n)
321 }
322 return &Graph{gNodes}
323 }
324
325 type nodePair struct {
326 src, dest *Node
327 }
328
329
330
331 func isNegative(n *Node) bool {
332 switch {
333 case n.Flat < 0:
334 return true
335 case n.Flat == 0 && n.Cum < 0:
336 return true
337 default:
338 return false
339 }
340 }
341
342 type locationMap struct {
343 s []Nodes
344 m map[uint64]Nodes
345 }
346
347 func (l *locationMap) add(id uint64, n Nodes) {
348 if id < uint64(len(l.s)) {
349 l.s[id] = n
350 } else {
351 l.m[id] = n
352 }
353 }
354
355 func (l locationMap) get(id uint64) Nodes {
356 if id < uint64(len(l.s)) {
357 return l.s[id]
358 } else {
359 return l.m[id]
360 }
361 }
362
363
364
365
366 func CreateNodes(prof *Profile, o *Options) (Nodes, locationMap) {
367 locations := locationMap{make([]Nodes, len(prof.Location)+1), make(map[uint64]Nodes)}
368 nm := make(NodeMap, len(prof.Location))
369 for _, l := range prof.Location {
370 lines := l.Line
371 if len(lines) == 0 {
372 lines = []Line{{}}
373 }
374 nodes := make(Nodes, len(lines))
375 for ln := range lines {
376 nodes[ln] = nm.findOrInsertLine(l, lines[ln], o)
377 }
378 locations.add(l.ID, nodes)
379 }
380 return nm.nodes(), locations
381 }
382
383 func (nm NodeMap) nodes() Nodes {
384 nodes := make(Nodes, 0, len(nm))
385 for _, n := range nm {
386 nodes = append(nodes, n)
387 }
388 return nodes
389 }
390
391 func (nm NodeMap) findOrInsertLine(l *Location, li Line, o *Options) *Node {
392 var objfile string
393 if m := l.Mapping; m != nil && m.File != "" {
394 objfile = m.File
395 }
396
397 if ni := nodeInfo(l, li, objfile, o); ni != nil {
398 return nm.FindOrInsertNode(*ni, o.KeptNodes)
399 }
400 return nil
401 }
402
403 func nodeInfo(l *Location, line Line, objfile string, o *Options) *NodeInfo {
404 if line.Function == nil {
405 return &NodeInfo{Address: l.Address}
406 }
407 ni := &NodeInfo{
408 Address: l.Address,
409 Lineno: int(line.Line),
410 Name: line.Function.Name,
411 }
412 ni.StartLine = int(line.Function.StartLine)
413 return ni
414 }
415
416
417 func (ns Nodes) Sum() (flat int64, cum int64) {
418 for _, n := range ns {
419 flat += n.Flat
420 cum += n.Cum
421 }
422 return
423 }
424
425 func (n *Node) addSample(dw, w int64, flat bool) {
426
427 if flat {
428 n.FlatDiv += dw
429 n.Flat += w
430 } else {
431 n.CumDiv += dw
432 n.Cum += w
433 }
434 }
435
436
437 func (g *Graph) String() string {
438 var s []string
439
440 nodeIndex := make(map[*Node]int, len(g.Nodes))
441
442 for i, n := range g.Nodes {
443 nodeIndex[n] = i + 1
444 }
445
446 for i, n := range g.Nodes {
447 name := n.Info.PrintableName()
448 var in, out []int
449
450 for _, from := range n.In {
451 in = append(in, nodeIndex[from.Src])
452 }
453 for _, to := range n.Out {
454 out = append(out, nodeIndex[to.Dest])
455 }
456 s = append(s, fmt.Sprintf("%d: %s[flat=%d cum=%d] %x -> %v ", i+1, name, n.Flat, n.Cum, in, out))
457 }
458 return strings.Join(s, "\n")
459 }
460
461
462
463
464 func (em EdgeMap) Sort() []*Edge {
465 el := make(edgeList, 0, len(em))
466 for _, w := range em {
467 el = append(el, w)
468 }
469
470 sort.Sort(el)
471 return el
472 }
473
474
475 func (em EdgeMap) Sum() int64 {
476 var ret int64
477 for _, edge := range em {
478 ret += edge.Weight
479 }
480 return ret
481 }
482
483 type edgeList []*Edge
484
485 func (el edgeList) Len() int {
486 return len(el)
487 }
488
489 func (el edgeList) Less(i, j int) bool {
490 if el[i].Weight != el[j].Weight {
491 return abs64(el[i].Weight) > abs64(el[j].Weight)
492 }
493
494 from1 := el[i].Src.Info.PrintableName()
495 from2 := el[j].Src.Info.PrintableName()
496 if from1 != from2 {
497 return from1 < from2
498 }
499
500 to1 := el[i].Dest.Info.PrintableName()
501 to2 := el[j].Dest.Info.PrintableName()
502
503 return to1 < to2
504 }
505
506 func (el edgeList) Swap(i, j int) {
507 el[i], el[j] = el[j], el[i]
508 }
509
510 func abs64(i int64) int64 {
511 if i < 0 {
512 return -i
513 }
514 return i
515 }
516
View as plain text