1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package graph
17
18 import (
19 "fmt"
20 "math"
21 "path/filepath"
22 "regexp"
23 "sort"
24 "strconv"
25 "strings"
26
27 "github.com/google/pprof/profile"
28 )
29
30 var (
31
32
33 javaRegExp = regexp.MustCompile(`^(?:[a-z]\w*\.)*([A-Z][\w\$]*\.(?:<init>|[a-z][\w\$]*(?:\$\d+)?))(?:(?:\()|$)`)
34
35
36 goRegExp = regexp.MustCompile(`^(?:[\w\-\.]+\/)+(.+)`)
37
38 goVerRegExp = regexp.MustCompile(`^(.*?)/v(?:[2-9]|[1-9][0-9]+)([./].*)$`)
39
40
41
42
43
44 cppRegExp = regexp.MustCompile(`^(?:[_a-zA-Z]\w*::)+(_*[A-Z]\w*::~?[_a-zA-Z]\w*(?:<.*>)?)`)
45 cppAnonymousPrefixRegExp = regexp.MustCompile(`^\(anonymous namespace\)::`)
46 )
47
48
49
50 type Graph struct {
51 Nodes Nodes
52 }
53
54
55 type Options struct {
56 SampleValue func(s []int64) int64
57 SampleMeanDivisor func(s []int64) int64
58 FormatTag func(int64, string) string
59 ObjNames bool
60 OrigFnNames bool
61
62 CallTree bool
63 DropNegative bool
64
65 KeptNodes NodeSet
66 }
67
68
69 type Nodes []*Node
70
71
72
73 type Node struct {
74
75 Info NodeInfo
76
77
78
79
80
81
82 Function *Node
83
84
85
86 Flat, FlatDiv, Cum, CumDiv int64
87
88
89
90 In, Out EdgeMap
91
92
93 LabelTags TagMap
94
95
96
97
98
99 NumericTags map[string]TagMap
100 }
101
102
103
104 func (n *Node) FlatValue() int64 {
105 if n.FlatDiv == 0 {
106 return n.Flat
107 }
108 return n.Flat / n.FlatDiv
109 }
110
111
112
113 func (n *Node) CumValue() int64 {
114 if n.CumDiv == 0 {
115 return n.Cum
116 }
117 return n.Cum / n.CumDiv
118 }
119
120
121
122 func (n *Node) AddToEdge(to *Node, v int64, residual, inline bool) {
123 n.AddToEdgeDiv(to, 0, v, residual, inline)
124 }
125
126
127
128 func (n *Node) AddToEdgeDiv(to *Node, dv, v int64, residual, inline bool) {
129 if n.Out[to] != to.In[n] {
130 panic(fmt.Errorf("asymmetric edges %v %v", *n, *to))
131 }
132
133 if e := n.Out[to]; e != nil {
134 e.WeightDiv += dv
135 e.Weight += v
136 if residual {
137 e.Residual = true
138 }
139 if !inline {
140 e.Inline = false
141 }
142 return
143 }
144
145 info := &Edge{Src: n, Dest: to, WeightDiv: dv, Weight: v, Residual: residual, Inline: inline}
146 n.Out[to] = info
147 to.In[n] = info
148 }
149
150
151 type NodeInfo struct {
152 Name string
153 OrigName string
154 Address uint64
155 File string
156 StartLine, Lineno int
157 Objfile string
158 }
159
160
161 func (i *NodeInfo) PrintableName() string {
162 return strings.Join(i.NameComponents(), " ")
163 }
164
165
166 func (i *NodeInfo) NameComponents() []string {
167 var name []string
168 if i.Address != 0 {
169 name = append(name, fmt.Sprintf("%016x", i.Address))
170 }
171 if fun := i.Name; fun != "" {
172 name = append(name, fun)
173 }
174
175 switch {
176 case i.Lineno != 0:
177
178 name = append(name, fmt.Sprintf("%s:%d", i.File, i.Lineno))
179 case i.File != "":
180
181 name = append(name, i.File)
182 case i.Name != "":
183
184 case i.Objfile != "":
185
186 name = append(name, "["+filepath.Base(i.Objfile)+"]")
187 default:
188
189 name = append(name, "<unknown>")
190 }
191 return name
192 }
193
194
195
196 type NodeMap map[NodeInfo]*Node
197
198
199 type NodeSet map[NodeInfo]bool
200
201
202
203
204
205
206
207
208
209 type NodePtrSet map[*Node]bool
210
211
212
213
214 func (nm NodeMap) FindOrInsertNode(info NodeInfo, kept NodeSet) *Node {
215 if kept != nil {
216 if _, ok := kept[info]; !ok {
217 return nil
218 }
219 }
220
221 if n, ok := nm[info]; ok {
222 return n
223 }
224
225 n := &Node{
226 Info: info,
227 In: make(EdgeMap),
228 Out: make(EdgeMap),
229 LabelTags: make(TagMap),
230 NumericTags: make(map[string]TagMap),
231 }
232 nm[info] = n
233 if info.Address == 0 && info.Lineno == 0 {
234
235
236 n.Function = n
237 return n
238 }
239
240 info.Address = 0
241 info.Lineno = 0
242 n.Function = nm.FindOrInsertNode(info, nil)
243 return n
244 }
245
246
247 type EdgeMap map[*Node]*Edge
248
249
250 type Edge struct {
251 Src, Dest *Node
252
253 Weight, WeightDiv int64
254
255
256
257 Residual bool
258
259 Inline bool
260 }
261
262
263
264 func (e *Edge) WeightValue() int64 {
265 if e.WeightDiv == 0 {
266 return e.Weight
267 }
268 return e.Weight / e.WeightDiv
269 }
270
271
272 type Tag struct {
273 Name string
274 Unit string
275 Value int64
276 Flat, FlatDiv int64
277 Cum, CumDiv int64
278 }
279
280
281
282 func (t *Tag) FlatValue() int64 {
283 if t.FlatDiv == 0 {
284 return t.Flat
285 }
286 return t.Flat / t.FlatDiv
287 }
288
289
290
291 func (t *Tag) CumValue() int64 {
292 if t.CumDiv == 0 {
293 return t.Cum
294 }
295 return t.Cum / t.CumDiv
296 }
297
298
299 type TagMap map[string]*Tag
300
301
302 func SortTags(t []*Tag, flat bool) []*Tag {
303 ts := tags{t, flat}
304 sort.Sort(ts)
305 return ts.t
306 }
307
308
309 func New(prof *profile.Profile, o *Options) *Graph {
310 if o.CallTree {
311 return newTree(prof, o)
312 }
313 g, _ := newGraph(prof, o)
314 return g
315 }
316
317
318
319
320 func newGraph(prof *profile.Profile, o *Options) (*Graph, map[uint64]Nodes) {
321 nodes, locationMap := CreateNodes(prof, o)
322 seenNode := make(map[*Node]bool)
323 seenEdge := make(map[nodePair]bool)
324 for _, sample := range prof.Sample {
325 var w, dw int64
326 w = o.SampleValue(sample.Value)
327 if o.SampleMeanDivisor != nil {
328 dw = o.SampleMeanDivisor(sample.Value)
329 }
330 if dw == 0 && w == 0 {
331 continue
332 }
333 for k := range seenNode {
334 delete(seenNode, k)
335 }
336 for k := range seenEdge {
337 delete(seenEdge, k)
338 }
339 var parent *Node
340
341 residual := false
342
343 labels := joinLabels(sample)
344
345 for i := len(sample.Location) - 1; i >= 0; i-- {
346 l := sample.Location[i]
347 locNodes := locationMap[l.ID]
348 for ni := len(locNodes) - 1; ni >= 0; ni-- {
349 n := locNodes[ni]
350 if n == nil {
351 residual = true
352 continue
353 }
354
355 if _, ok := seenNode[n]; !ok {
356 seenNode[n] = true
357 n.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, false)
358 }
359
360 if _, ok := seenEdge[nodePair{n, parent}]; !ok && parent != nil && n != parent {
361 seenEdge[nodePair{n, parent}] = true
362 parent.AddToEdgeDiv(n, dw, w, residual, ni != len(locNodes)-1)
363 }
364 parent = n
365 residual = false
366 }
367 }
368 if parent != nil && !residual {
369
370 parent.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, true)
371 }
372 }
373
374 return selectNodesForGraph(nodes, o.DropNegative), locationMap
375 }
376
377 func selectNodesForGraph(nodes Nodes, dropNegative bool) *Graph {
378
379 gNodes := make(Nodes, 0, len(nodes))
380 for _, n := range nodes {
381 if n == nil {
382 continue
383 }
384 if n.Cum == 0 && n.Flat == 0 {
385 continue
386 }
387 if dropNegative && isNegative(n) {
388 continue
389 }
390 gNodes = append(gNodes, n)
391 }
392 return &Graph{gNodes}
393 }
394
395 type nodePair struct {
396 src, dest *Node
397 }
398
399 func newTree(prof *profile.Profile, o *Options) (g *Graph) {
400 parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample))
401 for _, sample := range prof.Sample {
402 var w, dw int64
403 w = o.SampleValue(sample.Value)
404 if o.SampleMeanDivisor != nil {
405 dw = o.SampleMeanDivisor(sample.Value)
406 }
407 if dw == 0 && w == 0 {
408 continue
409 }
410 var parent *Node
411 labels := joinLabels(sample)
412
413 for i := len(sample.Location) - 1; i >= 0; i-- {
414 l := sample.Location[i]
415 lines := l.Line
416 if len(lines) == 0 {
417 lines = []profile.Line{{}}
418 }
419 for lidx := len(lines) - 1; lidx >= 0; lidx-- {
420 nodeMap := parentNodeMap[parent]
421 if nodeMap == nil {
422 nodeMap = make(NodeMap)
423 parentNodeMap[parent] = nodeMap
424 }
425 n := nodeMap.findOrInsertLine(l, lines[lidx], o)
426 if n == nil {
427 continue
428 }
429 n.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, false)
430 if parent != nil {
431 parent.AddToEdgeDiv(n, dw, w, false, lidx != len(lines)-1)
432 }
433 parent = n
434 }
435 }
436 if parent != nil {
437 parent.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, true)
438 }
439 }
440
441 nodes := make(Nodes, len(prof.Location))
442 for _, nm := range parentNodeMap {
443 nodes = append(nodes, nm.nodes()...)
444 }
445 return selectNodesForGraph(nodes, o.DropNegative)
446 }
447
448
449 func ShortenFunctionName(f string) string {
450 f = cppAnonymousPrefixRegExp.ReplaceAllString(f, "")
451 f = goVerRegExp.ReplaceAllString(f, `${1}${2}`)
452 for _, re := range []*regexp.Regexp{goRegExp, javaRegExp, cppRegExp} {
453 if matches := re.FindStringSubmatch(f); len(matches) >= 2 {
454 return strings.Join(matches[1:], "")
455 }
456 }
457 return f
458 }
459
460
461
462 func (g *Graph) TrimTree(kept NodePtrSet) {
463
464 oldNodes := g.Nodes
465 g.Nodes = make(Nodes, 0, len(kept))
466
467 for _, cur := range oldNodes {
468
469 if len(cur.In) > 1 {
470 panic("TrimTree only works on trees")
471 }
472
473
474 if _, ok := kept[cur]; ok {
475 g.Nodes = append(g.Nodes, cur)
476 continue
477 }
478
479
480
481 if len(cur.In) == 0 {
482 for _, outEdge := range cur.Out {
483 delete(outEdge.Dest.In, cur)
484 }
485 continue
486 }
487
488
489
490 if len(cur.In) != 1 {
491 panic("Get parent assertion failed. cur.In expected to be of length 1.")
492 }
493 var parent *Node
494 for _, edge := range cur.In {
495 parent = edge.Src
496 }
497
498 parentEdgeInline := parent.Out[cur].Inline
499
500
501 delete(parent.Out, cur)
502
503
504 for _, outEdge := range cur.Out {
505 child := outEdge.Dest
506
507 delete(child.In, cur)
508 child.In[parent] = outEdge
509 parent.Out[child] = outEdge
510
511 outEdge.Src = parent
512 outEdge.Residual = true
513
514
515
516 outEdge.Inline = parentEdgeInline && outEdge.Inline
517 }
518 }
519 g.RemoveRedundantEdges()
520 }
521
522 func joinLabels(s *profile.Sample) string {
523 if len(s.Label) == 0 {
524 return ""
525 }
526
527 var labels []string
528 for key, vals := range s.Label {
529 for _, v := range vals {
530 labels = append(labels, key+":"+v)
531 }
532 }
533 sort.Strings(labels)
534 return strings.Join(labels, `\n`)
535 }
536
537
538
539 func isNegative(n *Node) bool {
540 switch {
541 case n.Flat < 0:
542 return true
543 case n.Flat == 0 && n.Cum < 0:
544 return true
545 default:
546 return false
547 }
548 }
549
550
551
552
553 func CreateNodes(prof *profile.Profile, o *Options) (Nodes, map[uint64]Nodes) {
554 locations := make(map[uint64]Nodes, len(prof.Location))
555 nm := make(NodeMap, len(prof.Location))
556 for _, l := range prof.Location {
557 lines := l.Line
558 if len(lines) == 0 {
559 lines = []profile.Line{{}}
560 }
561 nodes := make(Nodes, len(lines))
562 for ln := range lines {
563 nodes[ln] = nm.findOrInsertLine(l, lines[ln], o)
564 }
565 locations[l.ID] = nodes
566 }
567 return nm.nodes(), locations
568 }
569
570 func (nm NodeMap) nodes() Nodes {
571 nodes := make(Nodes, 0, len(nm))
572 for _, n := range nm {
573 nodes = append(nodes, n)
574 }
575 return nodes
576 }
577
578 func (nm NodeMap) findOrInsertLine(l *profile.Location, li profile.Line, o *Options) *Node {
579 var objfile string
580 if m := l.Mapping; m != nil && m.File != "" {
581 objfile = m.File
582 }
583
584 if ni := nodeInfo(l, li, objfile, o); ni != nil {
585 return nm.FindOrInsertNode(*ni, o.KeptNodes)
586 }
587 return nil
588 }
589
590 func nodeInfo(l *profile.Location, line profile.Line, objfile string, o *Options) *NodeInfo {
591 if line.Function == nil {
592 return &NodeInfo{Address: l.Address, Objfile: objfile}
593 }
594 ni := &NodeInfo{
595 Address: l.Address,
596 Lineno: int(line.Line),
597 Name: line.Function.Name,
598 }
599 if fname := line.Function.Filename; fname != "" {
600 ni.File = filepath.Clean(fname)
601 }
602 if o.OrigFnNames {
603 ni.OrigName = line.Function.SystemName
604 }
605 if o.ObjNames || (ni.Name == "" && ni.OrigName == "") {
606 ni.Objfile = objfile
607 ni.StartLine = int(line.Function.StartLine)
608 }
609 return ni
610 }
611
612 type tags struct {
613 t []*Tag
614 flat bool
615 }
616
617 func (t tags) Len() int { return len(t.t) }
618 func (t tags) Swap(i, j int) { t.t[i], t.t[j] = t.t[j], t.t[i] }
619 func (t tags) Less(i, j int) bool {
620 if !t.flat {
621 if t.t[i].Cum != t.t[j].Cum {
622 return abs64(t.t[i].Cum) > abs64(t.t[j].Cum)
623 }
624 }
625 if t.t[i].Flat != t.t[j].Flat {
626 return abs64(t.t[i].Flat) > abs64(t.t[j].Flat)
627 }
628 return t.t[i].Name < t.t[j].Name
629 }
630
631
632 func (ns Nodes) Sum() (flat int64, cum int64) {
633 for _, n := range ns {
634 flat += n.Flat
635 cum += n.Cum
636 }
637 return
638 }
639
640 func (n *Node) addSample(dw, w int64, labels string, numLabel map[string][]int64, numUnit map[string][]string, format func(int64, string) string, flat bool) {
641
642 if flat {
643 n.FlatDiv += dw
644 n.Flat += w
645 } else {
646 n.CumDiv += dw
647 n.Cum += w
648 }
649
650
651 if labels != "" {
652 t := n.LabelTags.findOrAddTag(labels, "", 0)
653 if flat {
654 t.FlatDiv += dw
655 t.Flat += w
656 } else {
657 t.CumDiv += dw
658 t.Cum += w
659 }
660 }
661
662 numericTags := n.NumericTags[labels]
663 if numericTags == nil {
664 numericTags = TagMap{}
665 n.NumericTags[labels] = numericTags
666 }
667
668 if format == nil {
669 format = defaultLabelFormat
670 }
671 for k, nvals := range numLabel {
672 units := numUnit[k]
673 for i, v := range nvals {
674 var t *Tag
675 if len(units) > 0 {
676 t = numericTags.findOrAddTag(format(v, units[i]), units[i], v)
677 } else {
678 t = numericTags.findOrAddTag(format(v, k), k, v)
679 }
680 if flat {
681 t.FlatDiv += dw
682 t.Flat += w
683 } else {
684 t.CumDiv += dw
685 t.Cum += w
686 }
687 }
688 }
689 }
690
691 func defaultLabelFormat(v int64, key string) string {
692 return strconv.FormatInt(v, 10)
693 }
694
695 func (m TagMap) findOrAddTag(label, unit string, value int64) *Tag {
696 l := m[label]
697 if l == nil {
698 l = &Tag{
699 Name: label,
700 Unit: unit,
701 Value: value,
702 }
703 m[label] = l
704 }
705 return l
706 }
707
708
709 func (g *Graph) String() string {
710 var s []string
711
712 nodeIndex := make(map[*Node]int, len(g.Nodes))
713
714 for i, n := range g.Nodes {
715 nodeIndex[n] = i + 1
716 }
717
718 for i, n := range g.Nodes {
719 name := n.Info.PrintableName()
720 var in, out []int
721
722 for _, from := range n.In {
723 in = append(in, nodeIndex[from.Src])
724 }
725 for _, to := range n.Out {
726 out = append(out, nodeIndex[to.Dest])
727 }
728 s = append(s, fmt.Sprintf("%d: %s[flat=%d cum=%d] %x -> %v ", i+1, name, n.Flat, n.Cum, in, out))
729 }
730 return strings.Join(s, "\n")
731 }
732
733
734
735 func (g *Graph) DiscardLowFrequencyNodes(nodeCutoff int64) NodeSet {
736 return makeNodeSet(g.Nodes, nodeCutoff)
737 }
738
739
740
741 func (g *Graph) DiscardLowFrequencyNodePtrs(nodeCutoff int64) NodePtrSet {
742 cutNodes := getNodesAboveCumCutoff(g.Nodes, nodeCutoff)
743 kept := make(NodePtrSet, len(cutNodes))
744 for _, n := range cutNodes {
745 kept[n] = true
746 }
747 return kept
748 }
749
750 func makeNodeSet(nodes Nodes, nodeCutoff int64) NodeSet {
751 cutNodes := getNodesAboveCumCutoff(nodes, nodeCutoff)
752 kept := make(NodeSet, len(cutNodes))
753 for _, n := range cutNodes {
754 kept[n.Info] = true
755 }
756 return kept
757 }
758
759
760
761 func getNodesAboveCumCutoff(nodes Nodes, nodeCutoff int64) Nodes {
762 cutoffNodes := make(Nodes, 0, len(nodes))
763 for _, n := range nodes {
764 if abs64(n.Cum) < nodeCutoff {
765 continue
766 }
767 cutoffNodes = append(cutoffNodes, n)
768 }
769 return cutoffNodes
770 }
771
772
773
774 func (g *Graph) TrimLowFrequencyTags(tagCutoff int64) {
775
776 for _, n := range g.Nodes {
777 n.LabelTags = trimLowFreqTags(n.LabelTags, tagCutoff)
778 for s, nt := range n.NumericTags {
779 n.NumericTags[s] = trimLowFreqTags(nt, tagCutoff)
780 }
781 }
782 }
783
784 func trimLowFreqTags(tags TagMap, minValue int64) TagMap {
785 kept := TagMap{}
786 for s, t := range tags {
787 if abs64(t.Flat) >= minValue || abs64(t.Cum) >= minValue {
788 kept[s] = t
789 }
790 }
791 return kept
792 }
793
794
795
796 func (g *Graph) TrimLowFrequencyEdges(edgeCutoff int64) int {
797 var droppedEdges int
798 for _, n := range g.Nodes {
799 for src, e := range n.In {
800 if abs64(e.Weight) < edgeCutoff {
801 delete(n.In, src)
802 delete(src.Out, n)
803 droppedEdges++
804 }
805 }
806 }
807 return droppedEdges
808 }
809
810
811 func (g *Graph) SortNodes(cum bool, visualMode bool) {
812
813 switch {
814 case visualMode:
815
816 g.Nodes.Sort(EntropyOrder)
817 case cum:
818 g.Nodes.Sort(CumNameOrder)
819 default:
820 g.Nodes.Sort(FlatNameOrder)
821 }
822 }
823
824
825 func (g *Graph) SelectTopNodePtrs(maxNodes int, visualMode bool) NodePtrSet {
826 set := make(NodePtrSet)
827 for _, node := range g.selectTopNodes(maxNodes, visualMode) {
828 set[node] = true
829 }
830 return set
831 }
832
833
834 func (g *Graph) SelectTopNodes(maxNodes int, visualMode bool) NodeSet {
835 return makeNodeSet(g.selectTopNodes(maxNodes, visualMode), 0)
836 }
837
838
839 func (g *Graph) selectTopNodes(maxNodes int, visualMode bool) Nodes {
840 if maxNodes > 0 {
841 if visualMode {
842 var count int
843
844
845 for i, n := range g.Nodes {
846 tags := countTags(n)
847 if tags > maxNodelets {
848 tags = maxNodelets
849 }
850 if count += tags + 1; count >= maxNodes {
851 maxNodes = i + 1
852 break
853 }
854 }
855 }
856 }
857 if maxNodes > len(g.Nodes) {
858 maxNodes = len(g.Nodes)
859 }
860 return g.Nodes[:maxNodes]
861 }
862
863
864
865 func countTags(n *Node) int {
866 count := 0
867 for _, e := range n.LabelTags {
868 if e.Flat != 0 {
869 count++
870 }
871 }
872 for _, t := range n.NumericTags {
873 for _, e := range t {
874 if e.Flat != 0 {
875 count++
876 }
877 }
878 }
879 return count
880 }
881
882
883
884
885 func (g *Graph) RemoveRedundantEdges() {
886
887
888 for i := len(g.Nodes); i > 0; i-- {
889 n := g.Nodes[i-1]
890 in := n.In.Sort()
891 for j := len(in); j > 0; j-- {
892 e := in[j-1]
893 if !e.Residual {
894
895
896 break
897 }
898 if isRedundantEdge(e) {
899 delete(e.Src.Out, e.Dest)
900 delete(e.Dest.In, e.Src)
901 }
902 }
903 }
904 }
905
906
907
908 func isRedundantEdge(e *Edge) bool {
909 src, n := e.Src, e.Dest
910 seen := map[*Node]bool{n: true}
911 queue := Nodes{n}
912 for len(queue) > 0 {
913 n := queue[0]
914 queue = queue[1:]
915 for _, ie := range n.In {
916 if e == ie || seen[ie.Src] {
917 continue
918 }
919 if ie.Src == src {
920 return true
921 }
922 seen[ie.Src] = true
923 queue = append(queue, ie.Src)
924 }
925 }
926 return false
927 }
928
929
930
931 type nodeSorter struct {
932 rs Nodes
933 less func(l, r *Node) bool
934 }
935
936 func (s nodeSorter) Len() int { return len(s.rs) }
937 func (s nodeSorter) Swap(i, j int) { s.rs[i], s.rs[j] = s.rs[j], s.rs[i] }
938 func (s nodeSorter) Less(i, j int) bool { return s.less(s.rs[i], s.rs[j]) }
939
940
941
942
943
944 func (ns Nodes) Sort(o NodeOrder) error {
945 var s nodeSorter
946
947 switch o {
948 case FlatNameOrder:
949 s = nodeSorter{ns,
950 func(l, r *Node) bool {
951 if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv {
952 return iv > jv
953 }
954 if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv {
955 return iv < jv
956 }
957 if iv, jv := abs64(l.Cum), abs64(r.Cum); iv != jv {
958 return iv > jv
959 }
960 return compareNodes(l, r)
961 },
962 }
963 case FlatCumNameOrder:
964 s = nodeSorter{ns,
965 func(l, r *Node) bool {
966 if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv {
967 return iv > jv
968 }
969 if iv, jv := abs64(l.Cum), abs64(r.Cum); iv != jv {
970 return iv > jv
971 }
972 if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv {
973 return iv < jv
974 }
975 return compareNodes(l, r)
976 },
977 }
978 case NameOrder:
979 s = nodeSorter{ns,
980 func(l, r *Node) bool {
981 if iv, jv := l.Info.Name, r.Info.Name; iv != jv {
982 return iv < jv
983 }
984 return compareNodes(l, r)
985 },
986 }
987 case FileOrder:
988 s = nodeSorter{ns,
989 func(l, r *Node) bool {
990 if iv, jv := l.Info.File, r.Info.File; iv != jv {
991 return iv < jv
992 }
993 if iv, jv := l.Info.StartLine, r.Info.StartLine; iv != jv {
994 return iv < jv
995 }
996 return compareNodes(l, r)
997 },
998 }
999 case AddressOrder:
1000 s = nodeSorter{ns,
1001 func(l, r *Node) bool {
1002 if iv, jv := l.Info.Address, r.Info.Address; iv != jv {
1003 return iv < jv
1004 }
1005 return compareNodes(l, r)
1006 },
1007 }
1008 case CumNameOrder, EntropyOrder:
1009
1010 var score map[*Node]int64
1011 scoreOrder := func(l, r *Node) bool {
1012 if iv, jv := abs64(score[l]), abs64(score[r]); iv != jv {
1013 return iv > jv
1014 }
1015 if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv {
1016 return iv < jv
1017 }
1018 if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv {
1019 return iv > jv
1020 }
1021 return compareNodes(l, r)
1022 }
1023
1024 switch o {
1025 case CumNameOrder:
1026 score = make(map[*Node]int64, len(ns))
1027 for _, n := range ns {
1028 score[n] = n.Cum
1029 }
1030 s = nodeSorter{ns, scoreOrder}
1031 case EntropyOrder:
1032 score = make(map[*Node]int64, len(ns))
1033 for _, n := range ns {
1034 score[n] = entropyScore(n)
1035 }
1036 s = nodeSorter{ns, scoreOrder}
1037 }
1038 default:
1039 return fmt.Errorf("report: unrecognized sort ordering: %d", o)
1040 }
1041 sort.Sort(s)
1042 return nil
1043 }
1044
1045
1046
1047 func compareNodes(l, r *Node) bool {
1048 return fmt.Sprint(l.Info) < fmt.Sprint(r.Info)
1049 }
1050
1051
1052
1053
1054
1055
1056
1057
1058 func entropyScore(n *Node) int64 {
1059 score := float64(0)
1060
1061 if len(n.In) == 0 {
1062 score++
1063 } else {
1064 score += edgeEntropyScore(n, n.In, 0)
1065 }
1066
1067 if len(n.Out) == 0 {
1068 score++
1069 } else {
1070 score += edgeEntropyScore(n, n.Out, n.Flat)
1071 }
1072
1073 return int64(score*float64(n.Cum)) + n.Flat
1074 }
1075
1076
1077
1078
1079
1080
1081 func edgeEntropyScore(n *Node, edges EdgeMap, self int64) float64 {
1082 score := float64(0)
1083 total := self
1084 for _, e := range edges {
1085 if e.Weight > 0 {
1086 total += abs64(e.Weight)
1087 }
1088 }
1089 if total != 0 {
1090 for _, e := range edges {
1091 frac := float64(abs64(e.Weight)) / float64(total)
1092 score += -frac * math.Log2(frac)
1093 }
1094 if self > 0 {
1095 frac := float64(abs64(self)) / float64(total)
1096 score += -frac * math.Log2(frac)
1097 }
1098 }
1099 return score
1100 }
1101
1102
1103 type NodeOrder int
1104
1105
1106 const (
1107 FlatNameOrder NodeOrder = iota
1108 FlatCumNameOrder
1109 CumNameOrder
1110 NameOrder
1111 FileOrder
1112 AddressOrder
1113 EntropyOrder
1114 )
1115
1116
1117
1118
1119 func (e EdgeMap) Sort() []*Edge {
1120 el := make(edgeList, 0, len(e))
1121 for _, w := range e {
1122 el = append(el, w)
1123 }
1124
1125 sort.Sort(el)
1126 return el
1127 }
1128
1129
1130 func (e EdgeMap) Sum() int64 {
1131 var ret int64
1132 for _, edge := range e {
1133 ret += edge.Weight
1134 }
1135 return ret
1136 }
1137
1138 type edgeList []*Edge
1139
1140 func (el edgeList) Len() int {
1141 return len(el)
1142 }
1143
1144 func (el edgeList) Less(i, j int) bool {
1145 if el[i].Weight != el[j].Weight {
1146 return abs64(el[i].Weight) > abs64(el[j].Weight)
1147 }
1148
1149 from1 := el[i].Src.Info.PrintableName()
1150 from2 := el[j].Src.Info.PrintableName()
1151 if from1 != from2 {
1152 return from1 < from2
1153 }
1154
1155 to1 := el[i].Dest.Info.PrintableName()
1156 to2 := el[j].Dest.Info.PrintableName()
1157
1158 return to1 < to2
1159 }
1160
1161 func (el edgeList) Swap(i, j int) {
1162 el[i], el[j] = el[j], el[i]
1163 }
1164
1165 func abs64(i int64) int64 {
1166 if i < 0 {
1167 return -i
1168 }
1169 return i
1170 }
1171
View as plain text